<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3225418200403417535</id><updated>2011-12-18T21:27:58.853-08:00</updated><title type='text'>cmgresearch - Blogging about mobile development</title><subtitle type='html'>It's about having fun. If you're not having fun, why are you doing it?</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>20</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-8989615489343280744</id><published>2011-10-27T07:54:00.000-07:00</published><updated>2011-10-27T07:54:00.384-07:00</updated><title type='text'>Tanks! will soon be removed from the US app store</title><content type='html'>I'm sad to say that in the next couple of days I'll be removing both the free and paid versions of Tanks! from the US app store.&lt;br /&gt;
&lt;br /&gt;
I received an email from Apple last night saying that Atari had complained about the game saying that it infringed on their copyright. They appear to be of the opinion that they hold the rights to produce any game that involves driving a tank around...&lt;br /&gt;
&lt;br /&gt;
You can read the letter that Atari sent to apple &lt;a href="http://dl.dropbox.com/u/508075/iTunes%20App%20-%20Tanks%21%20Free%2010.17.11.pdf"&gt;here.&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Obviously I don't believe that Atari have a leg to stand on in this matter and taking the game down in no way implies any acceptance of their claims. But the amount of money that comes in from the game (&lt;$1 a day) does not justify paying for expensive lawyers.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-8989615489343280744?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/8989615489343280744/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2011/10/tanks-will-soon-be-removed-from-us-app.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/8989615489343280744'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/8989615489343280744'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2011/10/tanks-will-soon-be-removed-from-us-app.html' title='Tanks! will soon be removed from the US app store'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-5911418765713650631</id><published>2011-08-23T15:23:00.000-07:00</published><updated>2011-08-23T15:23:45.191-07:00</updated><title type='text'>Messing around with the XBOX Kinect</title><content type='html'>Just got back from a trip up to Scotland to visit my good friends Rob and Judy for the comedy festival.&lt;br /&gt;
&lt;br /&gt;
Since we had a bit of spare time on of the days, Rob and I decided that it would be good fun to try and hook his Kinect up to one of my games. Since I was part through porting the old space invaders game (&lt;a href="http://itunes.apple.com/us/app/aliens-invade/id324332367?mt=8"&gt;Aliens Invade&lt;/a&gt;) from the iPhone to the Mac app store it seemed like a good opportunity to have a bit of a play.&lt;br /&gt;
&lt;br /&gt;
We initially tried processing the raw depth data ourselves and using this to control the game. This was quite good for controlling the position, but we struggled to find a good algorithm for detecting when the user wanted to fire so decided that we needed a more powerful solution. Fortunately there is &lt;a href="http://kinecthesis.bakedmac.com/2011/01/11/installing-openni-kinect-drivers-and-nite-on-mac-os-x-10-6/"&gt;OpenNI and NITE&lt;/a&gt; which give you person tracking and pose detection.&lt;br /&gt;
&lt;br /&gt;
It works pretty well - and is great fun! Just run left and right to avoid the bullets and wave your hands in the air to fire.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe width="560" height="345" src="http://www.youtube.com/embed/9tw3GrgRDN4" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-5911418765713650631?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/5911418765713650631/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2011/08/messing-around-with-xbox-kinect.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/5911418765713650631'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/5911418765713650631'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2011/08/messing-around-with-xbox-kinect.html' title='Messing around with the XBOX Kinect'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/9tw3GrgRDN4/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-5636734797317827500</id><published>2010-12-03T09:27:00.000-08:00</published><updated>2010-12-03T12:20:41.360-08:00</updated><title type='text'>iAds Report</title><content type='html'>Thought it might be interesting to share some stats on how iAds are performing.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://itunes.apple.com/us/app/heart-rate-free/id401450171?mt=8"&gt;Heart Rate - Free&lt;/a&gt; has now been live for 2-3 weeks. Interestingly it somehow ended up in the wrong category, going into Entertainment when it was supposed to go into Health and Fitness. I don't know what impact that has had on downloads or advertising revenue.&lt;br /&gt;
&lt;br /&gt;
Over the two weeks it's been into the top 100 entertainment apps in various countries (it's doing particularly well in Hong Kong for some reason). In the US it got to around 60 ish before dropping out of the charts. I think it's now hovering around the 200 mark. To get into the top 100 free entertainment apps required over 1500 downloads a day in the US.&lt;br /&gt;
&lt;br /&gt;
Worldwide the application has been downloaded around 40-50,000 times. On its best performing day there were just under 9,500 downloads world wide. Currently it's doing around 1,000 downloads a day, but there is a definitely downward trend.&lt;br /&gt;
&lt;br /&gt;
When I was building the app I had already decided that it should be free so added AdMob adverts and then at the last moment decided that I might as well add iAds to the mix.&lt;br /&gt;
&lt;br /&gt;
As iAds are not available in all countries and having heard that fill rates could be very low I set the app up so that it would first try to get an iAd and if that failed it would pull down an AdMob ad. That way as iAds were rolled out to more countries the app would pick them automatically.&lt;br /&gt;
&lt;br /&gt;
The iAd reporting front end is a little bit clunky in how much you can slice and dice the data. What I'd really like to show is revenue broken out by time for the US - which is where iAds are mostly available (UK and France only started having inventory a few days ago). But here are the top 10 countries by ad requests since the app went live.&lt;br /&gt;
&lt;br /&gt;
&lt;table&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;th&gt;Country Name&lt;/th&gt;&lt;th&gt;Revenue&lt;/th&gt;&lt;th&gt;eCPM&lt;/th&gt;&lt;th&gt;Requests&lt;/th&gt;&lt;th&gt;Impressions&lt;/th&gt;&lt;th&gt;Fill Rate&lt;/th&gt;&lt;th&gt;CTR&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt; &lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Hong Kong&lt;/td&gt;&lt;td&gt;$0.00&lt;/td&gt;&lt;td&gt;$0.00&lt;/td&gt;&lt;td&gt;284868&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0.00%&lt;/td&gt;&lt;td&gt;0.00%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Unknown&lt;/td&gt;&lt;td&gt;$0.00&lt;/td&gt;&lt;td&gt;$0.00&lt;/td&gt;&lt;td&gt;97132&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0.00%&lt;/td&gt;&lt;td&gt;0.00%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;United States&lt;/td&gt;&lt;td&gt;$1,230.03&lt;/td&gt;&lt;td&gt;$29.75&lt;/td&gt;&lt;td&gt;83133&lt;/td&gt;&lt;td&gt;41348&lt;/td&gt;&lt;td&gt;49.74%&lt;/td&gt;&lt;td&gt;2.81%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;China&lt;/td&gt;&lt;td&gt;$0.00&lt;/td&gt;&lt;td&gt;$0.00&lt;/td&gt;&lt;td&gt;77595&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0.00%&lt;/td&gt;&lt;td&gt;0.00%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Israel&lt;/td&gt;&lt;td&gt;$0.00&lt;/td&gt;&lt;td&gt;$0.00&lt;/td&gt;&lt;td&gt;70080&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0.00%&lt;/td&gt;&lt;td&gt;0.00%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;France&lt;/td&gt;&lt;td&gt;$14.29&lt;/td&gt;&lt;td&gt;$8.87&lt;/td&gt;&lt;td&gt;58591&lt;/td&gt;&lt;td&gt;1612&lt;/td&gt;&lt;td&gt;2.75%&lt;/td&gt;&lt;td&gt;4.03%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Canada&lt;/td&gt;&lt;td&gt;$0.36&lt;/td&gt;&lt;td&gt;$2.86&lt;/td&gt;&lt;td&gt;48669&lt;/td&gt;&lt;td&gt;126&lt;/td&gt;&lt;td&gt;0.26%&lt;/td&gt;&lt;td&gt;2.38%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;UK/GB&lt;/td&gt;&lt;td&gt;$10.31&lt;/td&gt;&lt;td&gt;$7.38&lt;/td&gt;&lt;td&gt;24280&lt;/td&gt;&lt;td&gt;1397&lt;/td&gt;&lt;td&gt;5.75%&lt;/td&gt;&lt;td&gt;3.29%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Korea, Republic of&lt;/td&gt;&lt;td&gt;$0.00&lt;/td&gt;&lt;td&gt;$0.00&lt;/td&gt;&lt;td&gt;20992&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0.00%&lt;/td&gt;&lt;td&gt;0.00%&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
Fill rates for the US are actually pretty reasonable at just under 50%. eCPM is amazing at amost $30 when you compare it with the stats from AdMob over the same period of time:&lt;br /&gt;
&lt;br /&gt;
&lt;table&gt;&lt;tr&gt;&lt;th&gt;Revenu&lt;/th&gt;&lt;th&gt;eCPM&lt;/th&gt;&lt;th&gt;Request&lt;/th&gt;&lt;th&gt;Impressions&lt;/th&gt;&lt;th&gt;Fill Rate&lt;/th&gt;&lt;th&gt;CTR through rate&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;$675.38&lt;/td&gt;&lt;td&gt;$2.22&lt;/td&gt;&lt;td&gt;304,627&lt;/td&gt;&lt;td&gt;303,980&lt;/td&gt;&lt;td&gt;99.79%&lt;/td&gt;&lt;td&gt;4.51%&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;&lt;br /&gt;
The iAds are generating almost 15 times the eCPM.&lt;br /&gt;
&lt;br /&gt;
From its peak of $160 in a day revenue has declined quite rapidly. There's almost a direct correlation between the number of downloads in the US and amount of revenue. One problem with the heart rate app is that it doesn't really have anything currently to make the user come back to it very often. So the ad revenue tends to be tightly coupled to new users.&lt;br /&gt;
&lt;br /&gt;
The peak at the end of the chart corresponds to using the money generated from the AdMob ads on advertising the app in the US. This is definitely leading to more downloads, but it's not enough to keep the app in the charts in the entertainment category.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_jO69rW-vio4/TPkmgKrvCBI/AAAAAAAACDE/JNiiExyRxj4/s1600/Screen+shot+2010-12-03+at+17.16.38.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="130" src="http://1.bp.blogspot.com/_jO69rW-vio4/TPkmgKrvCBI/AAAAAAAACDE/JNiiExyRxj4/s320/Screen+shot+2010-12-03+at+17.16.38.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;a href="http://itunes.apple.com/us/app/heart-rate-free/id401450171?mt=8"&gt;Heart Rate - Free&lt;/a&gt;&amp;nbsp;is the first reasonably successful free app I've produced - I'm not a big fan of adverts in apps, but there does seem to be a compelling case for it. It will be interesting to see if the revenue continues when the app is really out in the wastelands and only getting 1 or 2 downloads a day.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-5636734797317827500?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/5636734797317827500/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/12/iads-report.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/5636734797317827500'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/5636734797317827500'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/12/iads-report.html' title='iAds Report'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_jO69rW-vio4/TPkmgKrvCBI/AAAAAAAACDE/JNiiExyRxj4/s72-c/Screen+shot+2010-12-03+at+17.16.38.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-3687100093178070743</id><published>2010-11-25T08:49:00.000-08:00</published><updated>2010-11-25T08:49:11.940-08:00</updated><title type='text'>Heart Rate Free - How it works</title><content type='html'>I've had a few emails asking for information on how my app &lt;a href="http://itunes.apple.com/us/app/heart-rate-free/id401450171?mt=8"&gt;Heart Rate - Free&lt;/a&gt; works.&lt;br /&gt;
&lt;br /&gt;
There's nothing particularly clever about it, it just uses the camera to pick up the slight change in the colour of the light coming from the flash as the blood flows in and out of the finger.&lt;br /&gt;
&lt;br /&gt;
You can make a few modifications to the code posted in the &lt;a href="http://cmgresearch.blogspot.com/2010/10/augmented-reality-on-iphone-with-ios40.html"&gt;Augmented Reality Post&lt;/a&gt; or download the &lt;a href="http://dl.dropbox.com/u/508075/SampleHeartRateApp.zip"&gt;demo code&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Setting up the capture session is pretty much the same as usual. The main differences are that we don't need to preview the image (it's not very interesting to look at...) and that we need to turn on the flash to illuminate the finger.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;// switch on the flash in torch mode
if([camera isTorchModeSupported:AVCaptureTorchModeOn]) {
 [camera lockForConfiguration:nil];
 camera.torchMode=AVCaptureTorchModeOn;
 [camera unlockForConfiguration];
}
&lt;/pre&gt;&lt;br /&gt;
We'll also set the frame size to low as we don't need high resolution images:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;[session setSessionPreset:AVCaptureSessionPresetLow];
&lt;/pre&gt;&lt;br /&gt;
The capture setup code looks like this now:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;// Create the AVCapture Session
session = [[AVCaptureSession alloc] init];
 
// Get the default camera device
AVCaptureDevice* camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if([camera isTorchModeSupported:AVCaptureTorchModeOn]) {
 [camera lockForConfiguration:nil];
 camera.torchMode=AVCaptureTorchModeOn;
 [camera unlockForConfiguration];
}
// Create a AVCaptureInput with the camera device
NSError *error=nil;
AVCaptureInput* cameraInput = [[AVCaptureDeviceInput alloc] initWithDevice:camera error:&amp;amp;error];
if (cameraInput == nil) {
 NSLog(@"Error to create camera capture:%@",error);
}
 
// Set the output
AVCaptureVideoDataOutput* videoOutput = [[AVCaptureVideoDataOutput alloc] init];
 
// create a queue to run the capture on
dispatch_queue_t captureQueue=dispatch_queue_create("catpureQueue", NULL);
 
// setup our delegate
[videoOutput setSampleBufferDelegate:self queue:captureQueue];
 
// configure the pixel format
videoOutput.videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey,
         nil];
// cap the framerate
videoOutput.minFrameDuration=CMTimeMake(1, 10);
// and the size of the frames we want
[session setSessionPreset:AVCaptureSessionPresetLow];
 
// Add the input and output
[session addInput:cameraInput];
[session addOutput:videoOutput];
 
// Start the session
[session startRunning];
&lt;/pre&gt;&lt;br /&gt;
In out capture callback we'll pull out the RGB values from the preview image:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
 // this is the image buffer
 CVImageBufferRef cvimgRef = CMSampleBufferGetImageBuffer(sampleBuffer);
 // Lock the image buffer
 CVPixelBufferLockBaseAddress(cvimgRef,0);
 // access the data
 int width=CVPixelBufferGetWidth(cvimgRef);
 int height=CVPixelBufferGetHeight(cvimgRef);
 // get the raw image bytes
 uint8_t *buf=(uint8_t *) CVPixelBufferGetBaseAddress(cvimgRef);
 size_t bprow=CVPixelBufferGetBytesPerRow(cvimgRef);
 // get the average red green and blue values from the image
 float r=0,g=0,b=0;
 for(int y=0; y&amp;lt;height; y++) {
  for(int x=0; x&amp;lt;width*4; x+=4) {
   b+=buf[x];
   g+=buf[x+1];
   r+=buf[x+2];
  }
  buf+=bprow;
 }
 r/=255*(float) (width*height);
 g/=255*(float) (width*height);
 b/=255*(float) (width*height);

 NSLog(@"%f,%f,%f", r, g, b);
}
&lt;/pre&gt;&lt;br /&gt;
If we run this and plot the values for red and green (blue is pretty uninteresting as it's almost zero) we can see that there's definitely some kind of change when the heart beats:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_jO69rW-vio4/TO6Ih7hUShI/AAAAAAAACC0/JbKh49JWEFk/s1600/Screen+shot+2010-11-25+at+16.01.39.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="193" src="http://4.bp.blogspot.com/_jO69rW-vio4/TO6Ih7hUShI/AAAAAAAACC0/JbKh49JWEFk/s320/Screen+shot+2010-11-25+at+16.01.39.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
The peaks in both the red and green values correspond to your pulse. We can combine the values into something a lot more convenient if we convert the RGB values to &lt;a href="http://en.wikipedia.org/wiki/HSL_and_HSV"&gt;HSV&lt;/a&gt; (hue, saturation, value) and just use the value of hue.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_jO69rW-vio4/TO6IoGyk1KI/AAAAAAAACC4/PVwdzm3MXxk/s1600/Screen+shot+2010-11-25+at+16.02.24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="211" src="http://3.bp.blogspot.com/_jO69rW-vio4/TO6IoGyk1KI/AAAAAAAACC4/PVwdzm3MXxk/s320/Screen+shot+2010-11-25+at+16.02.24.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
We can now see a definite pattern that corresponds to the heart beat. You'll probably want to do some filtering on the results from this - I'd suggest running it through a simple notch filter (highpass and lowpass filter) to get rid of DC component and apply a bit of smoothing to isolate the heart beat:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_jO69rW-vio4/TO6OSFgBHRI/AAAAAAAACC8/qstFB47fJNc/s1600/Screen+shot+2010-11-25+at+16.26.24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="241" src="http://1.bp.blogspot.com/_jO69rW-vio4/TO6OSFgBHRI/AAAAAAAACC8/qstFB47fJNc/s320/Screen+shot+2010-11-25+at+16.26.24.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
You can then pass these values into whatever mechanism you plan to use to detect and measure the beats.&lt;br /&gt;
&lt;br /&gt;
Here's the sample project in action:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_jO69rW-vio4/TO6SBzsS-XI/AAAAAAAACDA/sReGN6RWC-M/s1600/sample.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/_jO69rW-vio4/TO6SBzsS-XI/AAAAAAAACDA/sReGN6RWC-M/s320/sample.png" style="cursor: move;" width="213" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
You can download it from &lt;a href="http://dl.dropbox.com/u/508075/SampleHeartRateApp.zip"&gt;here&lt;/a&gt;. You'll need to replace the filtering with something a bit more sensible, the code I've put there is purely for demo purposes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-3687100093178070743?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/3687100093178070743/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/11/heart-rate-free-how-it-works.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/3687100093178070743'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/3687100093178070743'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/11/heart-rate-free-how-it-works.html' title='Heart Rate Free - How it works'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_jO69rW-vio4/TO6Ih7hUShI/AAAAAAAACC0/JbKh49JWEFk/s72-c/Screen+shot+2010-11-25+at+16.01.39.png' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-3395038287677810611</id><published>2010-11-19T04:11:00.000-08:00</published><updated>2010-11-19T04:11:19.558-08:00</updated><title type='text'>Heart Rate Free - just released on the iPhone</title><content type='html'>Saw an app that does this on the android platform so thought I 'd give it a go on iPhone.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It measures your heart rate by looking at the changes in skin colour as the blood flows in. It's only available on the iPhone 4 as it needs the flash in torch mode to work.&lt;br /&gt;
&lt;br /&gt;
You can get it here - it's free!&amp;nbsp;&lt;a href="http://itunes.apple.com/us/app/heart-rate-free/id401450171?mt=8"&gt;http://itunes.apple.com/us/app/heart-rate-free/id401450171?mt=8&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_jO69rW-vio4/TOZpTjDFtKI/AAAAAAAACCo/-RMbnwLt-ug/s1600/heart+rate.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/_jO69rW-vio4/TOZpTjDFtKI/AAAAAAAACCo/-RMbnwLt-ug/s320/heart+rate.png" width="213" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;
&lt;/div&gt;I'll do a blog post soon on how it works. It's surprisingly simple.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-3395038287677810611?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/3395038287677810611/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/11/heart-rate-free-just-released-on-iphone.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/3395038287677810611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/3395038287677810611'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/11/heart-rate-free-just-released-on-iphone.html' title='Heart Rate Free - just released on the iPhone'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_jO69rW-vio4/TOZpTjDFtKI/AAAAAAAACCo/-RMbnwLt-ug/s72-c/heart+rate.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-7171468049889634230</id><published>2010-10-10T02:52:00.000-07:00</published><updated>2010-10-10T02:56:06.728-07:00</updated><title type='text'>Augmented reality on the iPhone with iOS4.0</title><content type='html'>A long time ago I did a blog post on how to do AR on the iPhone. The code was written pre iOS4.0 and used the UIGetScreenImage function. I've now updated the code to use the new 4.0 features. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The code is available &lt;a href="http://dl.dropbox.com/u/508075/augmented_reality/ARDemo.zip"&gt;here&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
With the advent of iOS4.0 we now have proper access to the real time camera stream and the use of UIGetScreenImage is not allowed anymore.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To access the camera we now use the AV Foundation framework. There's a number of really good resources on how to use this. The WWDC video "Session 409 - Using the Camera with AV Foundation" along with the sample code from that session is a great resource (registered iPhone devs can download all the WWDC videos and sample code from iTunes by clicking here: &lt;a href="https://developer.apple.com/videos/wwdc/2010/"&gt;https://developer.apple.com/videos/wwdc/2010/&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To access the camera we need to add the following frameworks to our application:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;AVFoundation&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;CoreVideo&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;CoreMedia&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;The first step is to create an AVCaptureSession &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;// Create the AVCapture Session
session = [[AVCaptureSession alloc] init];
&lt;/pre&gt;If you want to show what the camera is seeing then you can create a view preview layer - this is handy if you just want to draw stuff on top of the camera preview. &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;// create a preview layer to show the output from the camera
AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:session];
previewLayer.frame = previewView.frame;
[previewView.layer addSublayer:previewLayer];
&lt;/pre&gt;In the code above I've assumed that you've created a view called previewView in your view hierarchy and positioned it where you want the camera preview to appear. The preview image will be aspect scaled to fit in the frame you specify.  The next step is to get hold of the capture device - here we just ask the OS to give us the default device that supports video. This will normally be the back camera. You can use the AVCaptureDevice class to query what devices are available using the devicesWithMediaType method. &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;// Get the default camera device
AVCaptureDevice* camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
&lt;/pre&gt;We now need to create a capture input with our camera &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;// Create a AVCaptureInput with the camera device
NSError *error=nil;
AVCaptureInput* cameraInput = [[AVCaptureDeviceInput alloc] initWithDevice:camera error:&amp;amp;error];
&lt;/pre&gt;And configure the capture output. This a bit more complicated. We allocate an instance of AVCaptureViewDataOutput and set ourself as the delegate to receive sample buffers - our class will need to implements the AVCaptureVideoDataOutputSampleBufferDelegate protocol. We need to provide a dispatch queue to run the output delegate on - you can't use the default queue.     We also set our desired pixel format - there are a couple of recommended output formats, CVPixelFormatType_32BGRA on all devices, kCVPixelFormatType_422YpCbCr8 on the 3G device and kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange on the 3GS and 4 devices. In this example we'll just use CVPixelFormatType_32BGRA. &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;// Set the output
AVCaptureVideoDataOutput* videoOutput = [[AVCaptureVideoDataOutput alloc] init];
 
// create a queue to run the capture on
dispatch_queue_t captureQueue=dispatch_queue_create("catpureQueue", NULL);
 
// setup our delegate
[videoOutput setSampleBufferDelegate:self queue:captureQueue];

// configure the pixel format
videoOutput.videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], (id)kCVPixelBufferPixelFormatTypeKey,
         nil];
&lt;/pre&gt;We can also configure the resolution of the images we want to capture. &lt;br /&gt;
&lt;table border="1"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Preset&lt;/td&gt;&lt;td&gt;3G&lt;/td&gt;&lt;td&gt;3GS&lt;/td&gt;&lt;td&gt;4 back&lt;/td&gt;&lt;td&gt;4 front&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;AVCaptureSessionPresetHigh&lt;/td&gt;&lt;td&gt;400x304&lt;/td&gt;&lt;td&gt;640x480&lt;/td&gt;&lt;td&gt;1280x720&lt;/td&gt;&lt;td&gt;640x480&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;AVCaptureSessionPresetMedium&lt;/td&gt;&lt;td&gt;400x304&lt;/td&gt;&lt;td&gt;480x360&lt;/td&gt;&lt;td&gt;480x360&lt;/td&gt;&lt;td&gt;480x360&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;AVCaptureSessionPresetLow&lt;/td&gt;&lt;td&gt;400x306&lt;/td&gt;&lt;td&gt;192x144&lt;/td&gt;&lt;td&gt;192x144&lt;/td&gt;&lt;td&gt;192x144&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;AVCaptureSessionPreset640x480&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;td&gt;640x480&lt;/td&gt;&lt;td&gt;640x480&lt;/td&gt;&lt;td&gt;640x480&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;AVCaptureSessionPreset1280x720&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;td&gt;1280x720&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;AVCaptureSessionPresetPhoto&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;td&gt;NA&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;pre class="cpp" name="code"&gt;// and the size of the frames we want
 [session setSessionPreset:AVCaptureSessionPresetMedium];
&lt;/pre&gt;Finally we need to add the input and outputs to the session and start it running: &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;// Add the input and output
 [session addInput:cameraInput];
 [session addOutput:videoOutput];
 
 // Start the session
 [session startRunning]; 
&lt;/pre&gt;We'll now start to receive images from the capture session on our implementation of didOutputSampleBuffer. &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
&lt;/pre&gt;To access the video data we need to use some of the functions from CoreVideo and CoreMedia. First we get hold of the image buffer from the sample buffer. We then need to lock the image buffer and then we can access its data. &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;// this is the image buffer
CVImageBufferRef cvimgRef = CMSampleBufferGetImageBuffer(sampleBuffer);
// Lock the image buffer
CVPixelBufferLockBaseAddress(cvimgRef,0);
// access the data
int width=CVPixelBufferGetWidth(cvimgRef);
int height=CVPixelBufferGetHeight(cvimgRef);
// get the raw image bytes
uint8_t *buf=(uint8_t *) CVPixelBufferGetBaseAddress(cvimgRef);
size_t bprow=CVPixelBufferGetBytesPerRow(cvimgRef);

.... do something useful with the image here .....

&lt;/pre&gt;With the image data you have a number of options, you can turn it into a UIImage: &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();
CGContextRef context=CGBitmapContextCreate(buf, width, height, 8, bprow, colorSpace, kCGBitmapByteOrder32Little|kCGImageAlphaNoneSkipFirst);
CGImageRef image=CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
UIImage *resultUIImage=[UIImage imageWithCGImage:image];
CGImageRelease(image);
&lt;/pre&gt;Or you can just process the data directly. The image will be stored in BGRA format, so each row of image data has pixels consisting of 4 bytes each - blue,green,red,alpha, blue,green,red,alpha, blue,green,red,alpha etc...&lt;br /&gt;
&lt;br /&gt;
If you're doing some image processing then you'll probably not bother with creating a UIImage and just go straight for crunching the raw bytes.&lt;br /&gt;
&lt;br /&gt;
I've put together a simple demo project &lt;a href="http://dl.dropbox.com/u/508075/augmented_reality/ARDemo.zip"&gt;here&lt;/a&gt;. The image processing is just a demo - don't try and use if for anything important.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-7171468049889634230?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/7171468049889634230/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/10/augmented-reality-on-iphone-with-ios40.html#comment-form' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/7171468049889634230'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/7171468049889634230'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/10/augmented-reality-on-iphone-with-ios40.html' title='Augmented reality on the iPhone with iOS4.0'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-2692422495527354730</id><published>2010-09-30T13:10:00.000-07:00</published><updated>2010-09-30T13:10:20.705-07:00</updated><title type='text'>Tanks! Review on Precentral</title><content type='html'>&lt;a href="http://www.precentral.net/review-tanks-webos"&gt;Precentral review of tanks.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-2692422495527354730?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/2692422495527354730/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/09/tanks-review-on-precentral.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/2692422495527354730'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/2692422495527354730'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/09/tanks-review-on-precentral.html' title='Tanks! Review on Precentral'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-4667639340319142612</id><published>2010-09-28T05:13:00.000-07:00</published><updated>2010-09-29T00:45:57.927-07:00</updated><title type='text'>NeHe OpenGL tutorials 1-6 ported to ES2.0</title><content type='html'>&lt;a href="http://cmgresearch.blogspot.com/2010/09/nehe-opengl-tutorial-2-3-and-4-ported.html"&gt;Tutorials 1,2,3 and 4&lt;/a&gt; - &lt;a href="http://dl.dropbox.com/u/508075/OpenGLNeHeTutorials/Tutorial2.zip"&gt;tutorial 2 source code&lt;/a&gt;, &lt;a href="http://dl.dropbox.com/u/508075/OpenGLNeHeTutorials/Tutorial3.zip"&gt;tutorial 3/4 source code&lt;/a&gt;&lt;br /&gt;
&lt;br&gt;&lt;br /&gt;
&lt;a href="http://cmgresearch.blogspot.com/2010/09/nehe-opengl-tutorial-5-ported-to-es20.html"&gt;Tutorial 5&lt;/a&gt; - &lt;a href="http://dl.dropbox.com/u/508075/OpenGLNeHeTutorials/Tutorial5.zip"&gt;tutorial 5 source code&lt;/a&gt;&lt;br /&gt;
&lt;br&gt;&lt;br /&gt;
&lt;a href="http://cmgresearch.blogspot.com/2010/09/nehe-opengl-tutorial-6-ported-to-es20.html"&gt;Tutorial 6&lt;/a&gt; - &lt;a href="http://dl.dropbox.com/u/508075/OpenGLNeHeTutorials/Tutorial6.zip"&gt;tutorial 6 source code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-4667639340319142612?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/4667639340319142612/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/09/nehe-opengl-tutorials-1-6-ported-to.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/4667639340319142612'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/4667639340319142612'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/09/nehe-opengl-tutorials-1-6-ported-to.html' title='NeHe OpenGL tutorials 1-6 ported to ES2.0'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-7087763660191919284</id><published>2010-09-28T05:11:00.000-07:00</published><updated>2010-09-28T05:11:33.126-07:00</updated><title type='text'>NeHe OpenGL tutorial 6 ported to ES2.0</title><content type='html'>Tutorial 6 is all about textures. In the original tutorial they use a rotating cube so we'll do the same.&lt;br /&gt;
&lt;br /&gt;
Fortunately the esUtils set of code comes with a handy function&amp;nbsp;esGenCube which will generate vertices, tex coords and indices for drawing a cube. We now have some setup code in our awakeFromNib function:&lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;// create a cube
esGenCube(1.0f, &amp;amp;cubeVertices, NULL, &amp;amp;cubeTexCoords, &amp;amp;cubeIndices);
// load up a texture
textureId=loadTexture(@"texture.png", -1, -1);
&lt;/pre&gt;To actually draw a texture to the screen we need to modify our shaders. Our vertex shader now needs to take a set of texture coords in addition to the vertice positions. It now looks like:&lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;attribute vec4 position;
attribute vec2 texcoord;
uniform mat4 mvp;

varying mediump vec2 v_texcoord;

void main()
{
    gl_Position = mvp * position;
    v_texcoord=texcoord;
}
&lt;/pre&gt;Notice that we now have an attribute called texcoord that we assign to a varying called v_texcoord.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Our fragment shader looks like this:&lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;varying mediump vec2 v_texcoord;
uniform sampler2D texture;

void main()
{
 gl_FragColor = texture2D(texture, v_texcoord);
}
&lt;/pre&gt;This now has a uniform that is of type sampler2D (this will receive our texture) and the varying v_texcoord that will get the tex coord from the vertex shader. It uses the texture2D function to get the correct value from the texture and we assign that to the fragment colour.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Our drawing code now looks like this:&lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;// reset our modelview to identity
esMatrixLoadIdentity(&amp;amp;modelView);
// translate back into the screen by 6 units and down by 1 unit
esTranslate(&amp;amp;modelView, 0.0f , 0.0f, -3.0f);
// rotate the triangle
esRotate(&amp;amp;modelView, rCube, 1.0, 1.0, 0.0);
// tell the GPU we want to use our program
glUseProgram(simpleProgram);
// create our new mvp matrix
esMatrixMultiply(&amp;amp;mvp, &amp;amp;modelView, &amp;amp;projection );
// set the mvp uniform
glUniformMatrix4fv(uniformMvp, 1, GL_FALSE, (GLfloat*) &amp;amp;mvp.m[0][0] );
// set the texture uniform
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureId);
glUniform1i(uniformTexture, 0);
// set the position vertex attribute with our triangle's vertices
glVertexAttribPointer(ATTRIB_POSITION, 3, GL_FLOAT, false, 0, cubeVertices);
glEnableVertexAttribArray(ATTRIB_POSITION);
// set the texture coords
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, 0, 0, cubeTexCoords);
glEnableVertexAttribArray(ATTRIB_TEXCOORD);

// and finally tell the GPU to draw our triangle!
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, cubeIndices);
&lt;/pre&gt;That's it! You can find the source code for this project &lt;a href="http://dl.dropbox.com/u/508075/OpenGLNeHeTutorials/Tutorial6.zip"&gt;here.&lt;/a&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_jO69rW-vio4/TKHa2wYypxI/AAAAAAAACCI/hA1yZV0IK9I/s1600/screenshot.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/_jO69rW-vio4/TKHa2wYypxI/AAAAAAAACCI/hA1yZV0IK9I/s320/screenshot.png" width="213" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-7087763660191919284?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/7087763660191919284/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/09/nehe-opengl-tutorial-6-ported-to-es20.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/7087763660191919284'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/7087763660191919284'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/09/nehe-opengl-tutorial-6-ported-to-es20.html' title='NeHe OpenGL tutorial 6 ported to ES2.0'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_jO69rW-vio4/TKHa2wYypxI/AAAAAAAACCI/hA1yZV0IK9I/s72-c/screenshot.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-4245571594621332128</id><published>2010-09-28T02:41:00.000-07:00</published><updated>2010-09-28T03:00:13.752-07:00</updated><title type='text'>Tanks! Mayhem - available for the iPhone/iPad</title><content type='html'>It's live! "Tanks! Mayhem" is on the app store &lt;a href="http://itunes.apple.com/gb/app/tanks-mayhem/id393743317?mt=8"&gt;here&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_jO69rW-vio4/TKG8lr0kHhI/AAAAAAAACB4/AScBSMAudPM/s1600/Screenshot+2010.09.16+19.10.29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="213" src="http://4.bp.blogspot.com/_jO69rW-vio4/TKG8lr0kHhI/AAAAAAAACB4/AScBSMAudPM/s320/Screenshot+2010.09.16+19.10.29.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_jO69rW-vio4/TKG8mQv_cSI/AAAAAAAACB8/DIZJKvfrgX0/s1600/Screenshot+2010.09.16+19.10.41.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="213" src="http://3.bp.blogspot.com/_jO69rW-vio4/TKG8mQv_cSI/AAAAAAAACB8/DIZJKvfrgX0/s320/Screenshot+2010.09.16+19.10.41.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_jO69rW-vio4/TKG8nVycUiI/AAAAAAAACCA/p77zK1Npnpw/s1600/Screenshot+2010.09.16+19.10.51.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="213" src="http://4.bp.blogspot.com/_jO69rW-vio4/TKG8nVycUiI/AAAAAAAACCA/p77zK1Npnpw/s320/Screenshot+2010.09.16+19.10.51.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_jO69rW-vio4/TKG8oG3q0DI/AAAAAAAACCE/M86V-4CD06s/s1600/Screenshot+2010.09.16+19.11.43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="213" src="http://4.bp.blogspot.com/_jO69rW-vio4/TKG8oG3q0DI/AAAAAAAACCE/M86V-4CD06s/s320/Screenshot+2010.09.16+19.11.43.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-4245571594621332128?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/4245571594621332128/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/09/tanks-mayhem-available-for-iphoneipad.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/4245571594621332128'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/4245571594621332128'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/09/tanks-mayhem-available-for-iphoneipad.html' title='Tanks! Mayhem - available for the iPhone/iPad'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_jO69rW-vio4/TKG8lr0kHhI/AAAAAAAACB4/AScBSMAudPM/s72-c/Screenshot+2010.09.16+19.10.29.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-1168890585738974323</id><published>2010-09-22T05:57:00.000-07:00</published><updated>2010-09-22T16:38:02.603-07:00</updated><title type='text'>NeHe OpenGL tutorial 5 ported to ES2.0</title><content type='html'>There's actually nothing specific to ES2.0 in this port - the code is exactly the same as in tutorial 3, but we are now passing all the data required to draw 3D shapes.
&lt;br&gt;
&lt;br&gt;
There are a couple of gotchas - for some reason in the default OpenGL template that xCode gives you does not have a depth buffer. So we need to add that to the initialisation in EAGLView.m
&lt;pre name="code" class="cpp"&gt;
  glGenRenderbuffers(1, &amp;depthbuffer);
  glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer);
  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, framebufferWidth, framebufferHeight);
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthbuffer);    
&lt;/pre&gt;
The other interesting thing in this project is that I've added rotation to the objects. This requires saving the model view matrix so we can rotate the objects individually. So in the code we now have:
&lt;pre name="code" class="cpp"&gt;
        // reset our modelview to identity
 esMatrixLoadIdentity(&amp;modelView);
 // translate back into the screen by 6 units and down by 1 unit
 esTranslate(&amp;modelView, 0.0f , -1.0f, -6.0f);
 // save the current modelView
 ESMatrix savedModelView=modelView;
 // rotate the triangle
 esRotate(&amp;modelView, rPyramid, 0.0, 1.0, 0.0);
 // tell the GPU we want to use our program
 glUseProgram(simpleProgram);
 // create our new mvp matrix
 esMatrixMultiply(&amp;mvp, &amp;modelView, &amp;projection );
 // set the mvp uniform
 glUniformMatrix4fv(uniformMvp, 1, GL_FALSE, (GLfloat*) &amp;mvp.m[0][0] );
  
  
 ... do the drawing of the pyramid

 // now draw a cube
  
 // translate up by 3 units
 esTranslate(&amp;savedModelView, 0.0f , 3.0f, 0.0f);
 // rotate by 45 degrees around the z axis
 esRotate(&amp;savedModelView, rCube, 0.0, 0.0, 1.0f);
 // create our new mvp matrix
 esMatrixMultiply(&amp;mvp, &amp;savedModelView, &amp;projection );
 // update the mvp uniform
 glUniformMatrix4fv(uniformMvp, 1, GL_FALSE, (GLfloat*) &amp;mvp.m[0][0] );

 ... do the drawing of the cube
&lt;/pre&gt;
We can save our matix pretty easily by just assigning it to another instance of the ESMatrix struct - this will copy all the values across so we can take a snapshot of it.
&lt;br&gt;
&lt;br&gt;
If you want to get clever you can re-implement the push and pop matrix functionality using std::stack.
&lt;br&gt;
&lt;br&gt;
Tutorial 5 is available &lt;a href="http://dl.dropbox.com/u/508075/OpenGLNeHeTutorials/Tutorial5.zip"&gt;here.&lt;/a&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_jO69rW-vio4/TJqTRp2cGMI/AAAAAAAACBo/oEdq7dgKB7U/s1600/Screenshot+2010.09.23+00.36.24.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 214px; height: 320px;" src="http://3.bp.blogspot.com/_jO69rW-vio4/TJqTRp2cGMI/AAAAAAAACBo/oEdq7dgKB7U/s320/Screenshot+2010.09.23+00.36.24.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5519886224797210818" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-1168890585738974323?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/1168890585738974323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/09/nehe-opengl-tutorial-5-ported-to-es20.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/1168890585738974323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/1168890585738974323'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/09/nehe-opengl-tutorial-5-ported-to-es20.html' title='NeHe OpenGL tutorial 5 ported to ES2.0'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_jO69rW-vio4/TJqTRp2cGMI/AAAAAAAACBo/oEdq7dgKB7U/s72-c/Screenshot+2010.09.23+00.36.24.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-5654961025398935620</id><published>2010-09-19T09:01:00.000-07:00</published><updated>2010-10-05T07:02:57.627-07:00</updated><title type='text'>NeHe OpenGL tutorial 2, 3 and 4 ported to ES 2.0</title><content type='html'>&lt;a href="http://cmgresearch.blogspot.com/2010/09/nehe-opengl-tutorials-1-6-ported-to.html"&gt;You can find an index to the ported tutorials here.&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
I recently created a new game for the iPhone (Tanks! Mayhem) and the Palm Pre/Pixie (Tanks!) as part of an effort to learn OpenGLES 2.0. I'd already become familiar with ES 1.0 using it in a previous game but felt that for the effect I wanted to achieve in the new game ES2.0 was the way to go. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
OpenGLES 2.0 introduces the idea of a programmable graphics pipeline. This basically puts you in charge of writing the code that is going to generate your graphics - very powerful, very generic, very overwhelming for someone getting started. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To get going I purchased the Blue Book (OpenGLES 2.0 programming guide) - which is great and I thoroughly recommend it - but one of the things that I found I really missed were some simple "this is how you'd do this thing you did in 1.0 in 2.0". Lots of books give you a complete implementation of 1.0 written as a shader which is interesting, but not much help for someone trying to get started. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When I was starting out learning ES 1.0 I found &lt;a href="http://iphonedevelopment.blogspot.com/2009/05/opengl-es-from-ground-up-table-of.html"&gt;Jeff Lamarche's excellent set of blog posts&lt;/a&gt; where he ported the &lt;a href="http://nehe.gamedev.net/"&gt;NeHe tutorials&lt;/a&gt; over to the iPhone. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
I'm going attempt to do the same for ES2.0 and port the tutorials over to ES2.0 on the iPhone - I'm going to assume that anyone reading this is familiar with ES1.0 - if you're not then why not run through his tutorials... &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Before we get started we're going to need some ground work. As I said, ES2.0 introduces us to a programmable pipeline, there are two areas of the pipeline that we can program, the coordinate processing, and the setting of pixel colours. In ES2.0 the code you run for these two areas is called a "vertex shader" and a "fragment shader". These two bits of code combine to make a "program". &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
As inputs, these programs take: &lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;b&gt;vertex attributes&lt;/b&gt; - consider these as the arrays you would normally pass into functions like glVertexPointer, glTexCoordPointer, glColorPointer etc...&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;uniforms&lt;/b&gt; - I think of these like constants &lt;/li&gt;
&lt;/ul&gt;Only the vertex shader part of the program gets access to the values from the vertex attributes. The vertex shader is run for each array index of the arrays you pass in for the vertex attributes. It also has access to the uniform values. The vertex shader is responsible for writing a value to the gl_Position variable - this variable tells OpenGL where to do it's drawing.   So for example if you wanted to draw a triangle you would create an array with three vertices specifying where you want your triangles to be drawn. You'd put that array into a vertex attribute and then issue the glDrawArrays with GL_TRIANGLES as the drawing type. Your vetex shader would get called for each vertice in the array and would take the coordinates and copy them into the gl_Position variable.&lt;br /&gt;
&lt;br /&gt;
OpenGL takes the coordinates you generated, applies any clipping, and then rasterises the triangle. For each pixel it generates during the rasterisation process the fragment shader is called to find out what color the pixel should be.&lt;br /&gt;
&lt;br /&gt;
That's what I think happens anyway...&lt;br /&gt;
&lt;br /&gt;
The fragment shader has access to all the uniforms. It also has access to varying variables. Varying variables are values that can be set by the vertex shader to be passed along to the fragment shader. They are called varying because they vary from pixel to pixel. The reason for this is because OpenGL interpolates the value they contain between each vertice.&lt;br /&gt;
&lt;br /&gt;
That's probably as clear as mud. So on with the lessons...&lt;br /&gt;
&lt;br /&gt;
Lesson 1 of the NeHe tutorials is all about getting set up to render. We're going to use the standard xCode OpenGLES template. We're also going to need a bunch of helper functions. I'm using the set of common files that come with the OpenGL ES2.0 programming guide. You can download them as part of the example code from here: http://www.opengles-book.com/downloads.html.   &lt;br /&gt;
&lt;h2&gt;Tutorial 2&lt;/h2&gt;Lesson 2 is where the fun actually starts. We're going to draw a white triangle and a sqaure on the screen - very exciting...&lt;br /&gt;
&lt;br /&gt;
It's at this point that I got stuck. where had glFrustrumf and glOrthof gone? How was I supposed to set up my projection and modelview matrices?&lt;br /&gt;
&lt;br /&gt;
ES 2.0 makes no assumptions about how you are going to transform your coordinates, you may choose to use a projection and modelview matrix. You may choose some completely arbitrary combination of matrices. You may even not be transforming your coordinates at all using a matrix.&lt;br /&gt;
&lt;br /&gt;
However, typically you will want to have a projection matrix and modelview matrix. The sample common code that comes with the OpenGL ES2.0 programming guide comes in really handy here. It has the missing frustrum and ortho functions along with a handy set of matrix functions.   Using the functions we can set up out project and modelview matrices:&lt;br /&gt;
&lt;br /&gt;
In your class file you'll need three variables: &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;ESMatrix projection;
ESMatrix modelView;
ESMatrix mvp;
&lt;/pre&gt;Projection will hold our projection matrix and modelView will hold our modelView matrix.   To set these up we'll use the following code - which should look very familiar: &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;const GLfloat zNear = 1, zFar = 600, fieldOfView = 40*M_PI/180.0;

esMatrixLoadIdentity(&amp;amp;projection);
GLfloat size = zNear * tanf(fieldOfView / 2.0); 
esFrustum(&amp;amp;projection, -size, size, -size / ((float) height / (float) width), size / ((float) height / (float) width), zNear, zFar); 

esMatrixLoadIdentity(&amp;amp;modelView);

glViewport(0, 0, width, height);  
&lt;/pre&gt;This will set our project matrix up with a perspective matrix and our modelView matrix up with identity.    To get our combined modelview project (mvp) matrix that we can use for transforming coordinates we'll do: &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;esMatrixMultiply(&amp;amp;mvp, &amp;amp;modelView, &amp;amp;projection );
&lt;/pre&gt;We now need our shader code. For this simple example we are going to be drawing triangles (in the original NeHe tutorial they draw a square as well - but ES is limited to triangles). This is pretty simple - our vertex shader will need to transform the input coordinates by our mvp matrix and our fragment shader just needs to set the pixels to whatever colour we choose.   So here's our vertex shader: &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;attribute vec4 position;
uniform mat4 mvp;

void main()
{
    gl_Position = mvp * position;
}
&lt;/pre&gt;So, what's going on here? We've got two inputs to the shader, a vertex attribute called position which is a vector and a uniform called mvp which is a 4x4 matrix. The position attribute will take our input vertex coordinates and the mvp matrix will be filled with the mvp matrix that we created from our modelview and projection matrices.   And here is our fragment shader: &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;uniform lowp vec4 color;

void main()
{
 gl_FragColor = color;
}
&lt;/pre&gt;Here we just have one input - a uniform that is a vector that we'll load up with the colour we want to set our pixels to.   To load these bits of code into the GPU we need to compile and link them. The first step is to compile the source code and attach the compiled shaders to a program object: &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &amp;amp;vertexShaderSource, NULL);
glCompileShader(vertexShader);

GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &amp;amp;fragmentShaderSource, NULL);
glCompileShader(fragmentShader);

GLuint simpleProgram = glCreateProgram();
glAttachShader(simpleProgram, vertexShader);
glAttachShader(simpleProgram, fragmentShader);
&lt;/pre&gt;We then need to bind our vertex attributes to specific locations. This will let us access them later. This step needs to be performed before the program is linked: &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;glBindAttribLocation(simpleProgram, ATTRIB_POSITION, "position");
&lt;/pre&gt;ATTIB_POSITION is an arbitrary value that you choose in your own code.   We can now link the program: &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;glLinkProgram(simpleProgram);
&lt;/pre&gt;And finally we can get the locations of our uniforms. &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;uniformMvp=glGetUniformLocation(simpleProgram, "mvp");
uniformColour=glGetUniformLocation(simpleProgram, "colour");
&lt;/pre&gt;We should now be fully setup and ready to draw! The following code draws a white triangle on the screen: &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;// vertices that we're going to draw
const GLfloat triVertices[] = { 
  0.0f, 1.0f, 0.0f, 
  -1.0f, -1.0f, 0.0f, 
  1.0f, -1.0f, 0.0f 
 }; 
// translate back into the screen by 6 units
esTranslatef(&amp;amp;modelView, 0.0f ,0.0f,-6.0f);
// tell the GPU we want to use our program
glUseProgram(simpleProgram);
// create our new mvp matrix
esMatrixMultiply(&amp;amp;mvp, &amp;amp;modelView, &amp;amp;projection );
// set the mvp uniform
glUniformMatrix4fv(uniformMvp, 1, GL_FALSE, (GLfloat*) &amp;amp;mvp.m[0][0] );
// set the colour uniform (r=1.0, g=1.0, b=1.0, a=1.0)
glUniform4f(uniformColour, 1.0, 1.0, 1.0, 1.0);
// set the position vertex attribute with our triangle's vertices
glVertexAttribPointer(ATTRIB_POSITION, 3, GL_FLOAT, false, 0, triVertices);
glEnableVertexAttribArray(ATTRIB_POSITION);
// and finally tell the GPU to draw our triangle!
glDrawArrays(GL_TRIANGLES, 0, 3);
&lt;/pre&gt;That's it - you've got a triangle on the screen.   You can download the full source code for tutorial 2 from &lt;a href="http://dl.dropbox.com/u/508075/OpenGLNeHeTutorials/Tutorial2.zip"&gt;here.&lt;/a&gt; The important code is found in Tutorial2ViewController.m in the loadShaders, setupView and drawFrame methods. It will draw both a triangle and a square on the screen. &lt;a href="http://1.bp.blogspot.com/_jO69rW-vio4/TJe3aKNLNHI/AAAAAAAACBY/VuxCrqYIgz4/s1600/Tutorial2ScreenShot.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img alt="" border="0" id="BLOGGER_PHOTO_ID_5519081528409928818" src="http://1.bp.blogspot.com/_jO69rW-vio4/TJe3aKNLNHI/AAAAAAAACBY/VuxCrqYIgz4/s320/Tutorial2ScreenShot.png" style="cursor: hand; cursor: pointer; display: block; height: 320px; margin: 0px auto 10px; text-align: center; width: 214px;" /&gt;&lt;/a&gt;   &lt;br /&gt;
&lt;h2&gt;Tutorial 3&lt;/h2&gt;We've actually already covered part of tutorial 3 by adding a uniform to our fragment shader that lets you set the colour. If you uncomment the line just after the comment that says "// change the colour uniform if you want to have a different colour" then your square will come out in green.   However, tutorial 3 is more about being able to change the colour at each vertex. How do we do this in ES2.0? This is where varying variables come in. Along with our position attribute we are going to add a colour attribute. The value of this will be assigned to a varying variable which will be passed into our fragment shader - the value of this variable will then be used to set the pixel value.   Our vertex shader now looks like: &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;attribute vec4 position;
attribute vec4 colour;

uniform mat4 mvp;

varying lowp vec4 vColour;

void main()
{
 gl_Position = mvp * position;
 vColour = colour;
}
&lt;/pre&gt;and our fragment shader like this: &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;varying lowp vec4 vColour;

void main()
{
 gl_FragColor = vColour;
}
&lt;/pre&gt;In our code we will now need a set of colours for the vertices: &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;const GLFloat triColours[] ={
 1.0f, 0.0f, 0.0f, 1.0f,
 0.0f, 1.0f, 0.0f, 1.0f,
 0.0f, 0.0f, 1.0f, 1.0f,
}
&lt;/pre&gt;And these will need to be set into our new colour attribute. Note that we no longer have a uniform called colour in our shaders. &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;// set the colour vertex attribute with our triangle's colours
glVertexAttribPointer(ATTRIB_COLOUR, 4, GL_FLOAT, false, 0, triColours);
glEnableVertexAttribArray(ATTRIB_COLOUR);
&lt;/pre&gt;You can download the full source code for tutorial 3 from &lt;a href="http://dl.dropbox.com/u/508075/OpenGLNeHeTutorials/Tutorial3.zip"&gt;here.&lt;/a&gt; The important code is found in Tutorial3ViewController,m in the loadShaders, setupView and drawFrame methods. It will draw both a triangle and a square on the screen with different colours at each vertex. &lt;a href="http://3.bp.blogspot.com/_jO69rW-vio4/TJe4wefsl_I/AAAAAAAACBg/QWU7D4pzJH4/s1600/Tutorial3Screenshot.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img alt="" border="0" id="BLOGGER_PHOTO_ID_5519083011325073394" src="http://3.bp.blogspot.com/_jO69rW-vio4/TJe4wefsl_I/AAAAAAAACBg/QWU7D4pzJH4/s320/Tutorial3Screenshot.png" style="cursor: hand; cursor: pointer; display: block; height: 320px; margin: 0px auto 10px; text-align: center; width: 214px;" /&gt;&lt;/a&gt;  &lt;br /&gt;
&lt;h2&gt;Tutorial 4&lt;/h2&gt;For tutorial 4, just use the esRotate function from the esUtils code - apply it to your modelview matrix before you ceate the mvp matrix. e.g. &lt;br /&gt;
&lt;pre class="cpp" name="code"&gt;// translate up by 3 units
esTranslate(&amp;amp;modelView, 0.0f , 3.0f, 0.0f);
// rotate by 45 degrees around the z axis
esRotate(&amp;amp;modelView, 45.0, 0.0, 0.0, 1.0f);
// create our new mvp matrix
esMatrixMultiply(&amp;amp;mvp, &amp;amp;modelView, &amp;amp;projection );
// update the mvp uniform
glUniformMatrix4fv(uniformMvp, 1, GL_FALSE, (GLfloat*) &amp;amp;mvp.m[0][0] );
&lt;/pre&gt;You can use either Tutorial 2 or 3 to play around with this. &lt;br /&gt;
&lt;br /&gt;
Over the coming weeks I'll port the rest of the tutorials - hopefully I'll learn some more as I do it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-5654961025398935620?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/5654961025398935620/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/09/nehe-opengl-tutorial-2-3-and-4-ported.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/5654961025398935620'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/5654961025398935620'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/09/nehe-opengl-tutorial-2-3-and-4-ported.html' title='NeHe OpenGL tutorial 2, 3 and 4 ported to ES 2.0'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_jO69rW-vio4/TJe3aKNLNHI/AAAAAAAACBY/VuxCrqYIgz4/s72-c/Tutorial2ScreenShot.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-4675263454880389610</id><published>2010-09-18T08:28:00.000-07:00</published><updated>2010-09-23T01:36:56.358-07:00</updated><title type='text'>Creating an OpenGL game for both the Palm and iOS</title><content type='html'>Long time no post - I've been in the lucky position of having too much paid work, so my own projects have had to be put to one side.
&lt;br&gt;&lt;br&gt;
However, I have had time to write and release a new game. "Tanks!" is now available for the Palm Pre/Pixie - you can get it by clicking here:
&lt;br&gt;&lt;br&gt;
&lt;a hef="http://developer.palm.com/appredirect/?packageid=com.cmgresearch.tankgame"&gt;&lt;img src="http://1.bp.blogspot.com/_jO69rW-vio4/TJTzMPL-EuI/AAAAAAAACAs/bLzUThMV4AY/s320/palm_button.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5518302834996417250" /&gt;&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
It should also be available as "Tanks! Mayhem" on the iPhone,iTouch and iPad devices in a week or two once Apple have finished the review process (it's getting harder and harder to find a free name on Apple app store - hence tacking on the Mayhem to the name).
&lt;br&gt;&lt;br&gt;
Originally I was only intending to release the game on the iPhone and only really started it as I wanted to learn OpenGLES 2.0 and needed something to do that on. I find it impossible to just learn something without having an end goal.
&lt;br&gt;&lt;br&gt;
The game does work on older phones though, it will fall back onto OpenGLES 1.0 for the rendering - this doesn't look as nice as the 2.0 version, but it's acceptable.
&lt;br&gt;&lt;br&gt;
I was about half way through development when got my hands on a Palm Pre Plus and the &lt;a href="http://developer.palm.com/index.php?option=com_content&amp;amp;view=article&amp;amp;id=1989&amp;amp;Itemid=20"&gt;Palm PDK&lt;/a&gt; (PDK - Plug-In Development Kit). The PDK lets you write applications in C/C++ and one of it's objectives is: "Easy porting of C/C++ applications to webOS, especially games that use SDL or OpenGL ES (1.1 or 2.0) for 3D graphics".
&lt;br&gt;&lt;br&gt;
Of course I was writing for iOS so the code I had was in Objective-C. You can easily write iOS code in C++ and you can also mix the two languages and have Objective-C++. However, I normally just stick to straight objective-C when writing iOS apps as you're normally doing a lot of UI work and all of the API is objective-C. This is a lot less true with OpenGL based games as the API is straightforward C, but I've actually come to quite like Objective-C now so I'd started out writing in that language.
&lt;br&gt;&lt;br&gt;
All that said, I now had a free phone and I'd committed to getting an app built for it by the end of September, so I bit the bullet and rewrote the core game engine and rendering components in C++. Thankfully the old knowledge all came back pretty quickly and it was surprisingly painless - though I wouldn't like to show the code to anyone I used to work with...
&lt;br&gt;&lt;br&gt;
It was fairly painless to get the game running on both the iPhone and the Palm devices. OpenGL games are fairly simple beasts, you generally have the following structure:
&lt;ol&gt;
&lt;li&gt;Set up the OpenGL system - this is normally specific to whatever platform you are developing for&lt;/li&gt;
&lt;li&gt;Read any user input - this is specific to the platform&lt;/li&gt;
&lt;li&gt;Update game state - generic&lt;/li&gt;
&lt;li&gt;Render frame&lt;/li&gt;
&lt;li&gt;Display the rendered frame - specific to the platform&lt;/li&gt;
&lt;li&gt;goto 2&lt;/li&gt;
&lt;/ol&gt;
Steps 3 and 4 are mostly completely generic and the same whatever platform you are coding for these two steps were ported to C++ and used on both platforms (I did need some #defines to cope with different include paths, but nothing too painful).
&lt;br&gt;
&lt;br&gt;
For areas that needed to be platform specific I either used a pure virtual base class and had specific implementations for each platform or for bits that needed objective-C I used a .mm file (objective-C++) for the iOS code and a .cpp file for the Palm code. Other places I just used a simple #ifdef PALM_BUILD and added that define to the my Palm build command.
&lt;br&gt;
&lt;br&gt;
Steps 1,2 and 5 are quite different. Palm uses &lt;a href="http://www.libsdl.org/cgi/docwiki.cgi/"&gt;SDL - Simple Directmedia Library&lt;/a&gt; for these areas. This is a nice simple framework - setting up OpenGL only takes a couple of lines of code and step 5 is just one line of code. On the iPhone this is fairly complex (though the project templates come with everything you need).
&lt;br&gt;
&lt;br&gt;
Step 2 is completely different on both platforms. Palm use SDL to wrap touches to the screen (touches map onto the SDL_MouseButton and SDL_MouseMotionEvent structures). iOS uses UITouch objects and has methods for touches begin, moved and ended. You can abstract away from this in your game engine, but it does require a bit of thinking.
&lt;br&gt;
&lt;br&gt;
If you have a game engine that does not rely on anything platform specific and is written in C++ or C then it &lt;i&gt;should&lt;/i&gt; be fairly easy to get running on multiple platforms. I'll now try and compare developing on the two platforms and try and highlight any issues or pitfalls.

&lt;h2&gt;Development&lt;/h2&gt;
For iPhone development we've got the xCode IDE this has improved a lot from the version that was available with the first iPhone SDK. One great feature is that they are now using LLVM as the front end for the compiler. This lets you do static analysis of your code to find error and memory leaks - very handy in a reference counting environment. The IDE is nicely integrated with the build system which makes compiling and fixing errors nice and simple.
&lt;br&gt;&lt;br&gt;
The Palm PDK doesn't come with an IDE or any plugins for the common IDEs (unfortunately customising xCode to use different compilers is incredibly difficult to do). I'm sure it should be possible to customise the Eclipse C++ plugin to use the Palm gcc arm compiler and tools but I didn't investigate this myself and couldn't find anything obvious on the web. In the end you have to go down to the command line and use MakeFiles. If you know what you're doing this is pretty painless, if you've never used a MakeFile before then this will initially be your idea of hell.
&lt;br&gt;&lt;br&gt;
You can use xCode for Palm development to run against the Palm simulator - but as far as I can tell you 'd need to keep the xCode project in sync with whatever you were using to build the app for the device. I didn't bother with the Palm simulator as I figured the iPhone device and iPhone simulator would give me just as much information on how the app would run on the Palm device as its own simulator.
&lt;br&gt;&lt;br&gt;
In the end lack of an IDE didn't effect me too much, I was developing for the iPhone at the same time so all my editing was done in xCode and I'd occasionally do a build for the Palm device as a sanity check. It did make the Palm specific areas a bit more painful to develop though - you need to run the make command, see what errors the compiler throws up and then go back to xCode and jump to the appropriate line etc...
&lt;br&gt;&lt;br&gt;
In conclusion I'd probably award this area to Apple - if anyone has some good ideas for IDEs for Palm PDK development then let me know.


&lt;h2&gt;Deploying to the device&lt;/h2&gt;
This is a difficult area to judge - once you've got the Apple process sussed out then getting your application running on a device is simply a case of hitting build and run. I would say if you are starting out though there are a number of hoops to jump through, you need to get various certificates installed, setup provisioning profiles etc... It's pretty complicated. Getting the application installed on the palm was simply a case of running a command on terminal to copy the app and supporting files over the usb cable to the device.
&lt;br&gt;
&lt;br&gt;
I think on the whole I'd have to award this to Palm, the Apple way has far too many points of failure and the number of posts I've seen on forums where people simply can't deploy to their device is just too many.


&lt;h2&gt;Debugging&lt;/h2&gt;
Debugging iPhone apps on both the device and simulator is done using xCode with it's gui front end to gdb (I'm sure most people don't even know or care that gdb is what's working under the hood).&lt;br&gt;
As far as I know the only way to do on device debugging with the Palm is ssh onto the device and run gdb on the command line. I didn't try this. My Palm debugging mostly consisted of the proper old school technique of adding strategic printfs throughout the code.
&lt;br&gt;
&lt;br&gt;
I'll have to award this to Apple. On device debugging in a nice IDE is priceless.

&lt;h2&gt;Packaging&lt;/h2&gt;
Once you've got a working app you need to package it up - you can't simply send Apple or Palm an email with your exe attached and let them get on with it.&lt;br&gt;
Once again, the Apple process when it works and you know what you are doing it pretty painless, but the forums attest to just how easy it is to screw it up and have no idea why it's not working. You need to create a provisioning profile for distribution, download and install it on xCode, create a new build configuration for distribution and update the code signing rules (which may or may not work depending on how xCode is feeling). All in all it's too easy to make mistakes and spend a day banging your head again the desk.
&lt;br&gt;
&lt;br&gt;
For the palm you need to create a directory with all the files you want to package up, an icon and an appinfo.json which is a 10 line file describing your app. There's then a command line app that creates a package for you to upload to Palm.&lt;br&gt;
This one definitely goes to Palm. For Apple there's too many ways you can screw it up.

&lt;h2&gt;App Submission&lt;/h2&gt;
You've got your final app all nicely packaged up. You're ready to ship - hooray! Now's the time to crack open the champagne and invite all your friends over..... In the real world, you are now entering a bit of a mysterious place... app submission processes tend to be a bit of black box. You put your app in one end, and then if you're lucky it comes out the other end on the app store.
&lt;br&gt;
&lt;br&gt;
Both Palm and Apple have a similar submission process, you fill out the app information, upload screen shots, set your pricing etc... Apple have changed their process slightly, you used to upload the application via the web, they've now integrated this process into xCode or you can use a seperate Application Loader app. Provided you have got the packaging section right and everything is signed correctly then mostly this all works ok - though when it goes wrong it does tend to be a bit of nightmare.
&lt;br&gt;
&lt;br&gt;
Can't really pick a winner both submission processes are pretty similar. One annoyance with the Palm process is you can't put landscape screenshots up - so all my screenshots are on their sides.
&lt;br&gt;
&lt;br&gt;
Another oddity with Palm is that it's very hard to self reject your own application and upload a new version. It shouldn't happen as we all test our apps very carefully before uploading. But it always seems that the minute after you've uploaded an application you find a major bug and have to re-upload a new version. With apple you just reject the binary and upload a new one. With Palm there's a manual process which required someone on Palm's side to accept your rejection request. Once they've done that then you can upload your new version.

&lt;h2&gt;Review process&lt;/h2&gt;
Reading the Palm forums I see pretty much the same complaints I see on the Apple forums about the process being opaque, not knowing what's happening etc...
&lt;br&gt;
&lt;br&gt;
Both Apple and Palm employ some automatic scanning to check for private APIs - a nice thing with Palms automatic scanner is that it tells you almost immediately if your app has a problem. Apple you have to wait for a reviewer to reject you - which can take a week or more.
&lt;br&gt;
&lt;br&gt;
I think on balance I'd probably go with Palm as they seem to be slightly more human, they also don't seem to have the same level of arbitrary rejections and I've not heard of anyone going into review limbo. They also seem to review apps a lot quicker than Apple.

&lt;h2&gt;Ad-hoc deployment/beta testing&lt;/h2&gt;
I wanted to test my built app on a Pixie as I know it's got a slower lower power CPU and GPU along with a smaller screen. Fortunately I was able to find someone with a Pixie (thanks Jason from Anaya Gaming!) and send him the packaged up Palm app to test. With an iOS app I would have had to get his device id, add it to my developer account and provisioning profile (which would use up 1 of my 100 device slots for the year) then send him a build along with the provisioning file. Once again this normally works, but when it doesn't it's a nightmare...&lt;br&gt;
Palm also have a beta testing area where you can upload your apps and then give the URL out to friendly users. Very handy.
&lt;br&gt;
&lt;br&gt;
I'll have to award this area to Palm.

&lt;h2&gt;Notable API differences&lt;/h2&gt;
As I've noted already the UI interaction area is completely different. Another area that was annoyingly different is Audio. iOS has &lt;a href="http://connect.creativelabs.com/openal/default.aspx"&gt;OpenAL&lt;/a&gt; for doing gaming audio. This is a powerful audio library that gives you positional audio along with lots of other effects. Palm comes as standard with SDL_Audio and SDL_Mixer - these are also quite usable but a bit limited. Fortunately you can compile OpenAL soft (thanks again Jason) and have it talk to SDL as it's back end. The rumour is that future versions of WebOS will come with OpenAL as standard which will be great.

&lt;h2&gt;Summary and Conclusion&lt;/h2&gt;
I wasn't really intending to write quite so much, especially not on a pub night, so I'd better conclude here. All in all it was quite an enjoyable experience porting the app across. It's pleasing to be able to use skills that have gone a bit rusty (getting back to the command line and using MakeFiles was great). If you've got game code that is based around OpenGL and is written in C++ or C then there aren't really any serious barriers to porting the code from iOS to the Palm PDK. Areas where you'll have difficulty are UI interaction and audio.
&lt;br&gt;
&lt;br&gt;
I would say though that if you have a lot of UIKit code in your iOS project you will have a bit of a mission. There's no easy way to bring that code and UI over to the PDK - also, you currently can't build hybrid apps, so everything has to be in SDL or OpenGL.
&lt;br&gt;
&lt;br&gt;
In total if I put all the time I'd spent developing and building the game together into one lump I think there's about 2-3 weeks of work in it. I think adding the Palm platform if you exclude the cost of porting my existing code to C++ added a few hours here and there, but nothing significant. Next up Android!
&lt;br&gt;
&lt;br&gt;
Here's how the game ended up on the Palm (this is version 1.0.4 which is waiting for approval right now). Apologies for the poor video quality:

                &lt;center&gt;
                &lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/bf7Ert1wkg4?fs=1&amp;amp;hl=en_US"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/bf7Ert1wkg4?fs=1&amp;amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;
                &lt;/center&gt;

And here's an iPhone screenshot:

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_jO69rW-vio4/TJUI5Qp-9_I/AAAAAAAACA8/xuAXrU1c3SY/s1600/3gsscreenshot.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 214px;" src="http://3.bp.blogspot.com/_jO69rW-vio4/TJUI5Qp-9_I/AAAAAAAACA8/xuAXrU1c3SY/s320/3gsscreenshot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5518326698229037042" /&gt;&lt;/a&gt;

And a Palm Pre Plus screenshot:

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_jO69rW-vio4/TJUJNuOlE_I/AAAAAAAACBE/-SYkxcsO3Uo/s1600/sc2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 214px;" src="http://3.bp.blogspot.com/_jO69rW-vio4/TJUJNuOlE_I/AAAAAAAACBE/-SYkxcsO3Uo/s320/sc2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5518327049764541426" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-4675263454880389610?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/4675263454880389610/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/09/tanks-for-palm-prepixie-and-iphoneipad.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/4675263454880389610'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/4675263454880389610'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/09/tanks-for-palm-prepixie-and-iphoneipad.html' title='Creating an OpenGL game for both the Palm and iOS'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_jO69rW-vio4/TJTzMPL-EuI/AAAAAAAACAs/bLzUThMV4AY/s72-c/palm_button.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-8689657607723569651</id><published>2010-07-10T02:01:00.000-07:00</published><updated>2010-07-10T02:08:05.006-07:00</updated><title type='text'>Shoot3D for the iPad/iPhone</title><content type='html'>Very pleased to get my first 3D game on the app store. It's been an interesting learning exercise, still feels as though I'm occasionally fumbling in the dark with only a vague understanding of how and why things are working.
&lt;br&gt;
The games section on the app store is a notoriously competitive place so I'm not sure how well I'll do with this, but it's a good break from doing normal utility apps.
&lt;br&gt;
I think it's a pretty good effort. You can download it from the &lt;a href="http://itunes.apple.com/us/app/shoot3d/id373950126?mt=8"&gt;app store here.&lt;/a&gt;:
&lt;br&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_jO69rW-vio4/TDg3qUGq0RI/AAAAAAAACAM/FAv2068BRY4/s1600/Screenshot+2010.06.26+20.04.59.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_jO69rW-vio4/TDg3qUGq0RI/AAAAAAAACAM/FAv2068BRY4/s320/Screenshot+2010.06.26+20.04.59.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5492200945669034258" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_jO69rW-vio4/TDg3rVENwLI/AAAAAAAACAU/-2mz4ZfZzA8/s1600/Screenshot+2010.06.26+20.07.45.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_jO69rW-vio4/TDg3rVENwLI/AAAAAAAACAU/-2mz4ZfZzA8/s320/Screenshot+2010.06.26+20.07.45.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5492200963107045554" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_jO69rW-vio4/TDg3sJfPCWI/AAAAAAAACAc/ho1_0pgVZEk/s1600/Screenshot+2010.06.26+20.18.56.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_jO69rW-vio4/TDg3sJfPCWI/AAAAAAAACAc/ho1_0pgVZEk/s320/Screenshot+2010.06.26+20.18.56.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5492200977179019618" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-8689657607723569651?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/8689657607723569651/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/07/shoot3d-for-ipadiphone.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/8689657607723569651'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/8689657607723569651'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/07/shoot3d-for-ipadiphone.html' title='Shoot3D for the iPad/iPhone'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_jO69rW-vio4/TDg3qUGq0RI/AAAAAAAACAM/FAv2068BRY4/s72-c/Screenshot+2010.06.26+20.04.59.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-8923691608897685178</id><published>2010-05-28T00:29:00.000-07:00</published><updated>2010-05-28T00:38:47.327-07:00</updated><title type='text'>iPad UK launch</title><content type='html'>Wondered down to have a look at the Apple store on Regent Street this morning.
&lt;br&gt;
Couple of amusing quotes.
&lt;br&gt;
From a guy in the queue who was already holding an iPad - "It's a great device! I've no idea what I'm going to use it for..."
&lt;br&gt;
And a random cyclist who stopped to find out what was happening - "I thought it was out already. Who have they got giving it out? It must be someone famous..."
&lt;br&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_jO69rW-vio4/S_9yK-i6upI/AAAAAAAAB_s/iSevyFh6c2g/s1600/IMG_0445.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_jO69rW-vio4/S_9yK-i6upI/AAAAAAAAB_s/iSevyFh6c2g/s320/IMG_0445.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5476221204819524242" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_jO69rW-vio4/S_9yKbUkiSI/AAAAAAAAB_k/vPa6NyhftxA/s1600/IMG_0444.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_jO69rW-vio4/S_9yKbUkiSI/AAAAAAAAB_k/vPa6NyhftxA/s320/IMG_0444.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5476221195364108578" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_jO69rW-vio4/S_9yJ72oUwI/AAAAAAAAB_c/Y26kgZ-ltcY/s1600/IMG_0443.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_jO69rW-vio4/S_9yJ72oUwI/AAAAAAAAB_c/Y26kgZ-ltcY/s320/IMG_0443.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5476221186917028610" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-8923691608897685178?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/8923691608897685178/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/05/ipad-uk-launch.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/8923691608897685178'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/8923691608897685178'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/05/ipad-uk-launch.html' title='iPad UK launch'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_jO69rW-vio4/S_9yK-i6upI/AAAAAAAAB_s/iSevyFh6c2g/s72-c/IMG_0445.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-9166019924127165713</id><published>2010-05-06T08:05:00.001-07:00</published><updated>2010-05-06T08:51:04.974-07:00</updated><title type='text'>BBC iPlayer - updated sample code</title><content type='html'>I've created a new demo project available &lt;a href="http://code.google.com/p/iplayerforipad/"&gt;here&lt;/a&gt;.
&lt;p&gt;
This one uses the RSS feeds from the iPLayer site to find the URLs and then uses the webview to play the actual movie.
&lt;p&gt;
Once again, you'll need the iPhone SDK and tool chain to build the project. The code is a bit hacked together so there's probably many bugs and memory leaks - use at your own risk...
&lt;p&gt;
For more details check this original &lt;a href="http://cmgresearch.blogspot.com/2010/05/getting-bbc-iplayer-working-on-ipad.html"&gt;blog post&lt;/a&gt;
&lt;p&gt;
&lt;a href="http://www.theregister.co.uk/2010/03/05/bbc_iphone/"&gt;This app will not be appearing on the iTunes store - I don't fancy getting a 10 page cease and desist order from the BBC....&lt;/a&gt;

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_jO69rW-vio4/S-La_9IQwEI/AAAAAAAAB_E/sTpc-g9_XFM/s1600/IMG_0008.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_jO69rW-vio4/S-La_9IQwEI/AAAAAAAAB_E/sTpc-g9_XFM/s320/IMG_0008.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5468173689857032258" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_jO69rW-vio4/S-LbACoumsI/AAAAAAAAB_M/wnDoyCqdH98/s1600/IMG_0009.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_jO69rW-vio4/S-LbACoumsI/AAAAAAAAB_M/wnDoyCqdH98/s320/IMG_0009.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5468173691335383746" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_jO69rW-vio4/S-LbAvWDSYI/AAAAAAAAB_U/LP6HFuAfSko/s1600/IMG_0007.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_jO69rW-vio4/S-LbAvWDSYI/AAAAAAAAB_U/LP6HFuAfSko/s320/IMG_0007.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5468173703336642946" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-9166019924127165713?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/9166019924127165713/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/05/bbc-iplayer-updated-sample-code.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/9166019924127165713'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/9166019924127165713'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/05/bbc-iplayer-updated-sample-code.html' title='BBC iPlayer - updated sample code'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_jO69rW-vio4/S-La_9IQwEI/AAAAAAAAB_E/sTpc-g9_XFM/s72-c/IMG_0008.PNG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-774390664056313227</id><published>2010-05-04T17:35:00.000-07:00</published><updated>2010-05-06T08:09:58.217-07:00</updated><title type='text'>Getting BBC iPlayer working on the iPad</title><content type='html'>(UPDATE - there's a new sample project available &lt;a href="http://cmgresearch.blogspot.com/2010/05/bbc-iplayer-updated-sample-code.html"&gt;here&lt;/a&gt;).
&lt;p&gt;
Flew back from the US on Monday carrying a shiny new iPad. One of my thoughts when I bought it was that it would make the perfect device for watching BBC iPlayer in bed - so you can imagine my dissappointment when I found that the iPlayer website kept asking me to install flash to play anything!
&lt;p&gt;
The weird thing is, iPlayer works on iPhones - you can even run iPhone apps that contain embedded UIWebViews, surf to the iPlayer web site and it all works!
&lt;p&gt;
Seems like it should be possible to get it working on the iPad. First step is to convince the bbc iPlayer website that the iPad is actually an iPhone so that it will give us the mobile website along with mp4 videos instead of flash.
&lt;p&gt;
Turns out that this is actually a bit tricky. Changing the User-Agent header field on the device is not exposed from any settings. So we need to create an iPad app with an embedded UIWebView. This will let us change the user agent to whatever we want.
&lt;p&gt;
It turns out that this is also a bit tricky. Fortunately there's a comprehensive post on how to do this here: "&lt;a href="http://www.icab.de/blog/2010/04/07/changing-the-headers-for-uiwebkit-http-requests/"&gt;Changing the headers for UIWebKit HTTP requests&lt;/a&gt;".
&lt;p&gt;
If you follow the instructions in that link and set the user agent to "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543 Safari/419.3" you can point your UIWebView to the bbc iplayer web site and get the iPhone compatible website.
&lt;p&gt;
Next problem is actually getting the video to play. The iPlayer website uses the "embed" tag for it's videos - this does not seem to play nicely on the iPad. To play a video we need to replace the embed tag with a proper html5 video tag.
&lt;p&gt;
Here's some simple code to do this - not the most elegant coding, but it's a quick hack to get something up and running:
&lt;p&gt;
&lt;pre name="code" class="cpp"&gt;
-(void) webViewDidFinishLoad:(UIWebView *)webView {
	NSString *js=@"document.getElementsByTagName(\"embed\").length;";
	NSString *embedCountStr=[webView stringByEvaluatingJavaScriptFromString:js];
	if(embedCountStr &amp;&amp; embedCountStr.length&gt;0) {
		int embedCountStr=[res intValue];
		if(embedCountStr&gt;0) {
			NSString *videoUrl=[webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName(\"embed\")[0].getAttribute(\"href\");"];
			NSLog(@"%@", videoUrl);
			[webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"document.write(\"&lt;html&gt;&lt;body style='margin:0;padding:0;'&gt;&lt;video width='%f' height='%f' controls&gt;&lt;source src='%@' type='video/mp4'&gt;&lt;/source&gt;&lt;/video&gt;&lt;/body&gt;&lt;/html&gt;\");", self.view.bounds.size.width, self.view.bounds.size.height, videoUrl]];
		}
	}
}
&lt;/pre&gt;
&lt;p&gt;
This code will find the embed tag on the page, pull out the video url from the href attribute and then write a video tag to UIWebView.
&lt;p&gt;
All in all it works pretty well. I've got pretty much full screen video working, device rotation all works. All it needs is a bit of tidying up and error checking.
&lt;p&gt;
Hopefully the bbc will update their iPlayer web code to support the iPad - but since it's not scheduled for release in the UK for some time I'd imagine they aren't in any hurry.
&lt;p&gt;
Obviously all this is only possible if you have access to the iPhone SDK and can build an application.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-774390664056313227?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/774390664056313227/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/05/getting-bbc-iplayer-working-on-ipad.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/774390664056313227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/774390664056313227'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/05/getting-bbc-iplayer-working-on-ipad.html' title='Getting BBC iPlayer working on the iPad'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-1496773805321697415</id><published>2010-01-31T11:06:00.000-08:00</published><updated>2010-03-04T01:38:53.445-08:00</updated><title type='text'>Getting started with Box2D</title><content type='html'>I recently gave a presentation at the &lt;a href="http://www.linkedin.com/groups?gid=1798655&amp;trk=myg_ugrp_ovr"&gt;LiDG&lt;/a&gt; on this - the slides can be found &lt;a href="http://dl.dropbox.com/u/508075/box2d_demos/presentation.zip"&gt;here&lt;/a&gt;.
&lt;br&gt;
&lt;br&gt;
The complete set of demo projects for this blog post can be found &lt;a href="http://dl.dropbox.com/u/508075/box2d_demos/Demos.zip"&gt;here&lt;/a&gt; (thanks to Terry for pointing out the missing files in the original set of zips).
&lt;br&gt;&lt;br&gt;
&lt;a href="http://www.box2d.org/"&gt;Box2D&lt;/a&gt; is an open source 2D physics engine that has proved popular with iPhone developers.
&lt;br&gt;&lt;br&gt;
One thing to bear in mind with Box2D is it is just a physics engine - all the drawing and rendering of the objects is down to you.
&lt;br&gt;&lt;br&gt;
The engine is tuned for meters-kilograms-seconds - so you can just feed in objects in pixels units, you should apply some kind of scaling to your objects so that they are in the 0.1 - 10 meter range.
&lt;br&gt;&lt;br&gt;
There are several concepts that it's useful to be familiar with:
&lt;ul&gt;
&lt;li&gt;Rigid Body/Body - chunk of matter that is so strong it cannot be compressed
&lt;/li&gt;&lt;li&gt;Shape - 2D pices of collision geometry that is attached to a body
&lt;/li&gt;&lt;li&gt;Friction, Restitution (bouncyness)
&lt;/li&gt;&lt;li&gt;Constraint - Physical connection that removes degrees of freedom from a body
&lt;/li&gt;&lt;li&gt;World - Collection of bodies, shapes, and constraints interacting together
&lt;/li&gt;
&lt;/ul&gt;

You can download Box2D from sourceforge or Google code. I chose to use a revision from the sourceforge repository that compiles for the iPhone and also matches the online documentation:
&lt;br&gt;&lt;br&gt;
svn co https://box2d.svn.sourceforge.net/svnroot/box2d box2d -r 210
&lt;br&gt;&lt;br&gt;
Once you've dwonloaded the code add the source and include folder to your xCode project as shown in the diagram:
&lt;br&gt;&lt;br&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_jO69rW-vio4/S2XVi5wjxzI/AAAAAAAAB98/oaxsE4MnLoM/s1600-h/Screen+shot+2010-01-31+at+14.09.13.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 115px;" src="http://2.bp.blogspot.com/_jO69rW-vio4/S2XVi5wjxzI/AAAAAAAAB98/oaxsE4MnLoM/s320/Screen+shot+2010-01-31+at+14.09.13.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5432983321089132338" /&gt;&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
To get Box2D building for the iPhone you need to add to your projects -DTARGET_OS_IPHONE additional CFLAGS

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_jO69rW-vio4/S2XfpxiBDII/AAAAAAAAB-M/SOvZsL7KqsE/s1600-h/Screen+shot+2010-01-31+at+14.52.48.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 154px;" src="http://4.bp.blogspot.com/_jO69rW-vio4/S2XfpxiBDII/AAAAAAAAB-M/SOvZsL7KqsE/s400/Screen+shot+2010-01-31+at+14.52.48.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5432994434256014466" /&gt;&lt;/a&gt;
&lt;br&gt;&lt;br&gt;
You'll also need to change your file extension from .m to .mm as Box2D uses C++.
&lt;br&gt;&lt;br&gt;
Now we can start to use the Box2D engine to do something useful. For this walkthrough we're going to create a simple pin-ball game.
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_jO69rW-vio4/S2XgUxJ92HI/AAAAAAAAB-U/NuAFDy_IrLI/s1600-h/Screen+shot+2010-01-31+at+14.55.35.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 275px; height: 400px;" src="http://4.bp.blogspot.com/_jO69rW-vio4/S2XgUxJ92HI/AAAAAAAAB-U/NuAFDy_IrLI/s400/Screen+shot+2010-01-31+at+14.55.35.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5432995172889516146" /&gt;&lt;/a&gt;
The image above shows our main game view - each of the objects are UIImageViews. We'll use these to show the locations of the objects in our physics simulation.
&lt;br&gt;&lt;br&gt;
The first thing we need to do is create our physics world. This object is responsible or managing the memory for our physics objects and simulating their interactions. Our physics world also has gravity.

&lt;pre name="code" class="cpp"&gt;
.h
// the world
b2World *myWorld;

.m
// create the world
b2AABB worldAABB;
worldAABB.lowerBound.Set(-100.0f, -100.0f);
worldAABB.upperBound.Set(100.0f, 100.0f);
b2Vec2 gravity(0.0f, -2.0f);
myWorld=new World(worldAABB, gravity, true);
&lt;/pre&gt;

When we want to simulate the world we move it forward by a time step. Ideally this time stap should be fast and regular. In the sample code below we'll be running our world 30 times a second.

&lt;pre name="code" class="cpp"&gt;
-(void) runWorld {
 // step the world forward
 myWorld-&gt;Step(1.0f/30.0f, 10, 10);
}

-(void) viewDidAppear:(BOOL)animated {
 [super viewDidAppear:animated];
 animationTimer=[NSTimer scheduledTimerWithTimeInterval:1.0f/30.0f target:self selector:@selector(runWorld) userInfo:nil repeats:YES];
}

-(void) viewDidDisappear:(BOOL)animated {
 [super viewDidDisappear:animated];
 [animationTimer invalidate];
 animationTimer=nil;
}
&lt;/pre&gt;
Now we've got our world we can add some objects to it. A body has position, rotation and velocity. You can apply forces, torques and impulses to them to.
&lt;br&gt;&lt;br&gt;
A body can be static or dynamic. Static bodies do not move and do not interact with other static bodies (we'll be using static bodies for the planets and edges of the pinball table.
&lt;br&gt;&lt;br&gt;
Bodies are the backbone for shapes. Two shapes on the same body never move relative to each other.
&lt;br&gt;&lt;br&gt;
A body is built from:
&lt;ul&gt;&lt;li&gt;A body definition&lt;/li&gt;
&lt;li&gt;Position and angle&lt;/li&gt;
&lt;li&gt;Damping (linear and angular)&lt;/li&gt;
&lt;li&gt;One or more shape definitions&lt;/li&gt;
&lt;li&gt;Friction&lt;/li&gt;
&lt;li&gt;Restitution&lt;/li&gt;
&lt;li&gt;Density&lt;/li&gt;
&lt;/ul&gt;
The code shown below creates a body for the ball bearing in our game:
&lt;pre name="code" class="cpp"&gt;
b2BodyDef ballBodyDef;
ballBodyDef.position.Set(S2W(self.ballView.center.x), 
    S2W(self.ballView.center.y));
ballBodyDef.linearDamping=0.5;
ballBodyDef.angularDamping=0.5;
ballBody=myWorld-&gt;CreateBody(&amp;ballBodyDef);
b2CircleDef ballShapeDef;
ballShapeDef.radius=
S2W(self.ballView.bounds.size.width/2);
ballShapeDef.density= 0.1f;
ballShapeDef.friction= 0.3f;
ballShapeDef.restitution = 0.2f;
ballBody-&gt;CreateShape(&amp;ballShapeDef);
ballBody-&gt;SetMassFromShapes();
&lt;/pre&gt;
To find out where our body is at any time we can query the body for its position, velocity, rotation and other attributes.

The code below updates our ball view to the position of the ball in the physics world:

&lt;pre name="code" class="cpp"&gt;
// find out where our ball should be
b2Vec2 position = ballBody-&gt;GetPosition();
ballView.center=CGPointMake(W2S(position.x), 
W2S(position.y));
&lt;/pre&gt;

If you run up Demo 1 you can see this code in action. You can reset the balls location by touching the screen.
&lt;br&gt;&lt;br&gt;
Obviousy we need some walls around the world to stop the ball escaping and the ball should be bouncing off the planets. We'll create static shapes for these objects. To create a static shape, just give it no mass - adding walls and the planets gives us Demo 2.
&lt;br&gt;&lt;br&gt;
The next step is to wire up the flippers. For this we need to create some joints. Box2D offers us a number of options for joining objects together:

&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_jO69rW-vio4/S2XXgVjbJcI/AAAAAAAAB-E/sN1oViv_lwY/s1600-h/Screen+shot+2010-01-31+at+14.17.47.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 335px;" src="http://4.bp.blogspot.com/_jO69rW-vio4/S2XXgVjbJcI/AAAAAAAAB-E/sN1oViv_lwY/s400/Screen+shot+2010-01-31+at+14.17.47.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5432985476033881538" /&gt;&lt;/a&gt;
For our purposed we'll be using a revolute joint to connect our flipper to the shoulders of the pinball table.

&lt;pre name="code" class="cpp"&gt;
 b2RevoluteJointDef jointDefLeftPaddle;
 jointDefLeftPaddle.Initialize(leftSlopeBody, leftPaddleBody,  
          b2Vec2(S2W(self.leftPaddleView.frame.origin.x), S2W(self.leftPaddleView.frame.origin.y)));
 jointDefLeftPaddle.lowerAngle = -0.25*b2_pi;
 jointDefLeftPaddle.upperAngle = 0.25*b2_pi;
 jointDefLeftPaddle.enableLimit = true;
 myWorld-&gt;CreateJoint(&amp;jointDefLeftPaddle);
&lt;/pre&gt;
To make our flippers move we'll apply torque (rotational force) to the body when the user touches the screen. We'll also play a sound at the same time.
&lt;pre name="code" class="cpp"&gt;
if(touchPos.y&gt;400 &amp;&amp; touchPos.x&lt;90) {
 leftPaddleBody-&gt;ApplyTorque(-500);
 AudioServicesPlaySystemSound(flipperSound);
} else if(touchPos.y&gt;400 &amp;&amp; touchPos.x&gt;210) {
 rightPaddleBody-&gt;ApplyTorque(500);
 AudioServicesPlaySystemSound(flipperSound);
} 
&lt;/pre&gt;
Demo 3 shows this all working. We've now got an almost complete game. All that's left it to detect when the ball collides with the planets and other things.
&lt;br&gt;&lt;br&gt;
Box2D create contacts to manage collisions between shapes. These have:
&lt;ul&gt;&lt;li&gt;
Contact point - where the shapes touch
&lt;/li&gt;&lt;li&gt;Contact normal - points from shape1 to shape2
&lt;/li&gt;&lt;li&gt;Contact seperation - opposite of penetration
&lt;/li&gt;&lt;li&gt;Normal Force - collision intensity
&lt;/li&gt;&lt;li&gt;Tangent Force - friction force
&lt;/li&gt;&lt;/ul&gt;
To listen for contacts we need to create a C++ class that implements the b2ContactListener interface.
This has the following methods:
&lt;ul&gt;&lt;li&gt;
Add - a contact has been added
&lt;/li&gt;&lt;li&gt;Persist - a contact persisted for more than one timestep
&lt;/li&gt;&lt;li&gt;Remove - contact removed
&lt;/li&gt;&lt;li&gt;Result - computed results for contact
&lt;/li&gt;
&lt;/ul&gt;
One thing to be aware of is that within a single timestep you might receive multiple notifications of contacts between objects. In my code I simply set a flag to indicate what the ball has collided with and process it after the world step has completed.

&lt;pre name="code" class="cpp"&gt;
class ContactListener : public b2ContactListener {
 PinBallViewController *pinBallViewController;
public:
 ContactListener(PinBallViewController *controller) : pinBallViewController(controller) {}
 void Result(const b2ContactResult* point) { 
  b2Body *body1=point-&gt;shape1-&gt;GetBody();
  b2Body *body2=point-&gt;shape2-&gt;GetBody();
  
  [pinBallViewController contactBetween:body1 andBody:body2 withForce:point-&gt;normalImpulse];
 }
};

 contactListener=new ContactListener(self);
 myWorld-&gt;SetContactListener(contactListener);
&lt;/pre&gt;

With this in place we can detect when the ball hits the planets, the walls and the flippers. The &lt;a href="http://dl.dropbox.com/u/508075/box2d_demos/Demos.zip"&gt;complete demo (Demo4)&lt;/a&gt; shows this all working. We've got a basic working game in a couple of 100 lines of code!
&lt;br&gt;&lt;br&gt;
Feel free to reuse any of the code in your own projects.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-1496773805321697415?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/1496773805321697415/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/01/getting-started-with-box2d.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/1496773805321697415'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/1496773805321697415'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/01/getting-started-with-box2d.html' title='Getting started with Box2D'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_jO69rW-vio4/S2XVi5wjxzI/AAAAAAAAB98/oaxsE4MnLoM/s72-c/Screen+shot+2010-01-31+at+14.09.13.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-1136945557344440274</id><published>2010-01-28T04:02:00.000-08:00</published><updated>2010-01-28T04:16:29.041-08:00</updated><title type='text'>Tablet+iPhones = Console+Controllers</title><content type='html'>Here's an idea. Everyone has an iPhone, get yourself a tablet and use gamekit to connect everything together.
&lt;br&gt;&lt;br&gt;
You've now got a a bunch of wireless controllers with accelerometers (the iPhones) and a console (the tablet).
&lt;br&gt;&lt;br&gt;
I think there's a lot of potential here for the kind of games we've been seeing on the Wii.
&lt;br&gt;&lt;br&gt;
Or how about having the tablet as a card table and each iPhone holding you hand of cards...
&lt;br&gt;&lt;br&gt;
Interesting times.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-1136945557344440274?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/1136945557344440274/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/01/tabletiphones-consolecontrollers.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/1136945557344440274'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/1136945557344440274'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/01/tabletiphones-consolecontrollers.html' title='Tablet+iPhones = Console+Controllers'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3225418200403417535.post-6966591495621440600</id><published>2010-01-01T09:57:00.001-08:00</published><updated>2010-10-12T01:30:10.503-07:00</updated><title type='text'>Augmented Reality on the iPhone - how to</title><content type='html'>&lt;span style="font-weight: bold;"&gt;This is now superseded by the changes in iOS4 that give you access to the camera. &lt;a href="http://cmgresearch.blogspot.com/2010/10/augmented-reality-on-iphone-with-ios40.html"&gt;Use the code from this blog post instead.&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3225418200403417535-6966591495621440600?l=cmgresearch.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cmgresearch.blogspot.com/feeds/6966591495621440600/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cmgresearch.blogspot.com/2010/01/augmented-reality-on-iphone-how-to_01.html#comment-form' title='21 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/6966591495621440600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3225418200403417535/posts/default/6966591495621440600'/><link rel='alternate' type='text/html' href='http://cmgresearch.blogspot.com/2010/01/augmented-reality-on-iphone-how-to_01.html' title='Augmented Reality on the iPhone - how to'/><author><name>Chris</name><uri>http://www.blogger.com/profile/15244470582042837701</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>21</thr:total></entry></feed>
