Game Development Community

Retina how-to (iPhone 4 resolution)

by Jonas Dahlman · in iTorque 2D · 04/20/2011 (11:05 am) · 18 replies

This is a guide on how to get Retina display support working with iTorque. All of this has been done in my game (in review at the moment) and it works well :)

Huge thanks goes to Scott Wilson for helping me with this and also for Dean Parkers resources.

It should be noted that in addition to all the following code I made my game universal with Dean Parker's resource.

*** 1. First we need to implement detection of iPhone 4. We definitely don't want to detect iPhone4 specifically. Instead a code detects if the resolution is scaled up and that code might come handy in the future for other devices.

In iPhoneWindow.mm in function Platform::initWindow() we have to fix our main window size to fit with the shiny high-resolution of iPhone4. Add the following just before gScreenOrientation = Con::getIntVariable( "$pref::iDevice::ScreenOrientation" );

if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2) {
	platState.windowSize.x = 960;
	platState.windowSize.y = 640;
}

Just after glView = [[iPhoneOGLVideo alloc] initWithFrame: rect] add the following:

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { 
	Con::setVariable( "$platform", "ipad" );    
} else {   
        if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2) {
	        Con::setVariable( "$platform", "iphone4" );
	        glView.contentScaleFactor = [[UIScreen mainScreen] scale];		
	}  else {  
	        Con::setVariable( "$platform", "iphone" );  
}

That will set values to the variable $platform and it will be used to determine what resources to load later on.

*** 2. My game was locked to portrait mode so I had to do some adjustments to the render view as the render window was in wrong place (edit this if you find any problems with this)

In bool::setScreenOrientation() inside if( platState.portrait )
{ brackets replace:

glPositionPoint.x = point.x;
glPositionPoint.y = point.y;

with:

if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2) {
	glPositionPoint.x = 480;
	glPositionPoint.y = 160;
} 
else 
{
	glPositionPoint.x = point.x;
	glPositionPoint.y = point.y;
}

*** 3. I had to to change the some values in the touch events in iPhoneOGLVideo.mm to make it the touches work in portrait mode.
I'm not sure how it works in landscape so try first before implement code. If you find issues however you need to correct calculation to make it work in the following functions...

The functions that needs to be edited is (void)touchesEnded, (void)touchesBegan, (void)touchesMoved.

In all these functions do a check for upscaled resolution:

NSUInteger touchCount = 0; // Leave this as it is

if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2 && platState.portrait) {       
        // Add the existing code in the function here but you need to add before the code
        // Need to fix coordinates if Retina display!
	point.x *= 2;
	point.y = ((point.y - 400) * 2) + 480;
}
else {
    // Add the existing code in the function here as it was
}

*** 3. Change the camera size in your levels to fit the resolution, 640x960 if portrait and 960x640 if landscape.

*** 4. Change your GUI's to fit the new resolution. You can use Dean Parker's great script here for that.

*** 5. Use variables to determine what resources to load. For example in main.cs I check what platform and load specific levels and resources for all the platforms based on the variable $platform.

A note should be added that I had a drop of whopping 20fps on iPhone4 device when using this code. I'm not actually sure why as the code was tried first with hardly anything in the scene. It might be related to the detection of scaling in the touch events but I left as it was as it was working ;)

If you're making your game universal try to use your iPad images for iPhone4 as this will make your app smaller.


#1
04/20/2011 (1:46 pm)
Sounds good Jonas. And this has worked for you?



#2
04/21/2011 (12:55 am)
Jonas, thanks for posting, I didn't see any drop in FPS on either of our retina games, however, the processor is having to push around larger gfx so there will be some impact.
#3
04/21/2011 (5:03 pm)
@Scott,
This was the system you implemented for retina display? And to obtain retina display objects, did you simply set the png's resolution to 326 ppi (I believe that is 4g resolution)?
#4
05/05/2011 (1:58 pm)
I came to this document with the same error of only seeing a 4th of the screen. I can run iphone4 in simulator mode just fine (added that to the commonConfig), just not the device.

When i add the
glView.contentScaleFactor = [[UIScreen mainScreen] scale];
line, I get a black screen.

Did you experience that at all?

#5
05/05/2011 (2:21 pm)
Hello Mark!

Have you set your GUI (especially the mainscreen GUI) to the correct Retina resolution and also the correct resolution for scene level?

If you're seing fourth of a screen that means that you have come a long way as that means you´re actually using the Retina display.

Also as a backup try, change the resolution in Commonconfig.xml (if you're using that) if the above fails...

You can't run the Retina display in the simulator. (I think)

I recommend following Dean Parkers guide on how to make your app universal if you haven't done that!
#6
05/05/2011 (2:39 pm)
I have Dean's stuff working just fine. iphone and ipad both look great. I am using ipad images for everything and scaling down.

When I deploy to iphone4 I see a fourth of a screen, so woot! for making it this far... :)

Now I am at the point where I am trying to debug this 4th of screen error so I jumped into your resource above.

As soon as I change the scale to 2.0, it goes black. Using the code above.

All my printfs and echos are showing the correct Portrait resolutions. If I comment out the contentScaleFactor I can at least get it back to the 1/4th the screen.

Any thoughts?

Side note: Simulator I just added a third option of running at 640x960 and it appears to be fine.
#7
05/05/2011 (2:50 pm)
Ugh, Changed to landscape and seems to display (stretched). Thanks Jonas
#8
05/05/2011 (4:01 pm)
Awesome. Its alive!

I had to make 2 changes from your example:

1. In the orientation function I adjusted the x/y to:
point.x = 320;
point.y = 0;


2. In the touch functions I adjusted the x/y with:
point.x *= 2;
point.y = ((point.y - 480) * 2);


Thanks again for your help!
#9
05/07/2011 (9:46 pm)
@Jonas. Thanks for posting this resource.

I am also trying to get Retina Display, and I have some comments about your code:

1. You can get Retina Display in the iOS simulator by enabling the menu item Hardware->Devive->iPhone (Retina)

2.
Quote: Add the following just before gScreenOrientation = Con::getIntVariable( "$pref::iDevice::ScreenOrientation" );

if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2) {
	platState.windowSize.x = 960;
	platState.windowSize.y = 640;
}

First, adding this piece of code in that place has no effect at all, since the variable platState is initialized again 2 lines below. Here is the first few lines of the function as in iTGB 1.4.1

void Platform::initWindow(const Point2I &initialSize, const char *name)
{
   dSprintf(platState.appWindowTitle, sizeof(platState.appWindowTitle), name);
	
	platState.windowSize.x = IPHONE_DEFAULT_RESOLUTION_X;
	platState.windowSize.y = IPHONE_DEFAULT_RESOLUTION_Y;
 
    //Get screen orientation prefs //Based on 0 Landscape, 1 Portrait
	gScreenOrientation = Con::getIntVariable( "$pref::iDevice::ScreenOrientation" );
	gScreenUpsideDown = Con::getBoolVariable( "$pref::iPhone::ScreenUpsideDown" );

    platState.windowSize = initialSize;

Note: The initialization of platState.windowSize in the second and third lines is also useless. To see what the Point2I &initialSize variable contains at this point in the program execution please see this thread.

www.garagegames.com/community/forums/viewthread/124734/1#comment-801182

This Point2I variable contains coordinates that were initialized from information got from script. For my case, I am running a iPhone portrait app, this is 320x480.

Doing a bit of research of how to enable Retina Display using OpenGL ES, I came to this site

developer.apple.com/library/ios/#documentation/2DDrawing/Conceptual/DrawingPrint...

See the section "Drawing High-Resolution Content Using OpenGL ES". Here's a quote

Quote:To enable high-resolution drawing, you must change the scale factor of the view you use to present your OpenGL ES content. Changing the contentScaleFactor property of your view from 1.0 to 2.0 triggers a matching change to the scale factor of the underlying CAEAGLLayer object.

So, from my understanding (and I am no expert in iOS/OpenGL ) all that *should* be needed is indeed this change

if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2)  
            glView.contentScaleFactor = 2;

Also, I may be totally wrong about this but I think that the change you suggest to

if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2) {
	platState.windowSize.x = 960;
	platState.windowSize.y = 640;
}

does not need to be done. This is because the resolution of the iPhone is in points, not pixels. It's always 320x480 points (in retina 1 point is 2 pixels). Anyway, what I get (with this change and without it) is always the result of 1/4th of the screen.

Changing the coordinates later (for the central point) in the orientation function to

point.x = 320;
point.y = 0;

has for me just the effect of displaying the 1/4th of the screen at the top right (instead of bottom left).

From the script side I am following Dean Parker's resource for Universal App, which is working fine in my app (I get iPad resolution).

@Mark, Scott

Do you have any insight on this? Thanks.
#10
05/07/2011 (11:51 pm)
If you are seeing 1/4 of the screen, you need to make sure your scale factor is getting adjusted correctly:

glView.contentScaleFactor = [[UIScreen mainScreen] scale];

It appears you are trying to do this already. Just add a few prints to make sure that code is getting called and that it indeed is being set to 2. When I first got that to work, the screen on the device turned black and that is where the positioning/orientation comes in.
#11
05/08/2011 (4:55 pm)
Besides the change to the scale factor of the view to 2.0, there is another change that must be done, because the OpenGL viewport is still being set to 320x480 (for the portrait case).

This is done with something like

glViewport( 0, 0, 640, 960 );

I made a change for a quick test with these values and it does Retina display in the simulator.
#12
06/03/2011 (6:38 am)
I followed this and the resources supplied by Dean but I could get nothing more than a black screen out of torque. I noticed some (seemingly very minor) differences in the 1.5 preview to what's expected in these instructions. I have not toyed with a 1.4 to see the results.

My question is... Even after completing this enhancement to the engine, will I still be left with scaled images/sprites or will I get full resolution (960x640)? How will I enable the full resolution - is it just a matter of setting the camera properly in TGB?

I'm trying to decide if I really want to fight through the engine to make sure this works completely.
#13
06/03/2011 (8:02 am)
I also noticed that some of the changes made to the 1.5 Preview affect this resource. The important takeaway is that there is a reason I am looking into how others have approached this concept. My own inclination is generally this resource can still work, but it will take some understanding of the engine to get it to work.
#14
06/03/2011 (9:19 am)
My plan, as I get time, is to take the initial universal changes and apply them as working chunks. Once that's done I -should- have enough to go on to apply the rest of this. If it works I'll provide some diffs as I'll be working with a stock engine.

Or maybe I should be working in the 1.4 engine and diff the completion of the 1.4 to the stock 1.5 ... hmmm ...
#15
06/03/2011 (10:05 am)
Hey Joe,
I haven't tried 1.5, so I can only answer that this resource works in 1.4. Images will only be scaled if you choose to scale them. There seems to be 2 options. You save your images out as large as you need (ipad or retina) and then scale down for the 640x480 resolution OR you make multiple image maps and save different images for each resolution. The only way you would end up with scaled up images is if you saved your images for 640x480 and used those in your 960x640.

I would play with the coordinates in the orientation function as your view may just be pushed outside of the screen.

Good luck! Even with 1.4 its not a straight forward process.

#16
06/03/2011 (2:13 pm)
@All

I added a new resource that shows how to enable Retina

www.garagegames.com/community/resources/view/21048

Notes:

1) I used iTorque2D 1.4.1
2) It was based in this original post but with differences:

a) Do not define any hardcoded values in Platform::initWindow()
b) There is no need to change setScreenOrientation()at all; the code there just seems about right.
c) Instead other changes are made in PlatformVideo.cc
#17
06/05/2011 (7:21 am)
Mich wrote:

Quote:
I also noticed that some of the changes made to the 1.5 Preview affect this resource. The important takeaway is that there is a reason I am looking into how others have approached this concept. My own inclination is generally this resource can still work, but it will take some understanding of the engine to get it to work.

If you look closely at his original post, you will note that certain letters have been made bold.

I got the message, Mich. ;)
#18
06/13/2011 (6:00 am)
@jonas

I still don't own an iphone 4 and could not get this working. Great job on the retina work and your article.