Thoughts on supporting @2x resolution ..

Moderator
Posts: 3,570
Joined: 2003.06
Post: #16
Most of our 2D graphics are done in Illustrator as well, but some of them are 2D renders from a 3D scene, so even then we have lots of room for scaling.

Oh yeah, adding iPad is lots of extra fun! What I've been doing is laying out HUD/menu graphics from the center of the screen. That way when re-laying-out graphics for iPad, I can adjust for the different aspect of the display. Since on iPad I support all four orientations, I figure I might as well do that on iPhone as well (this is for a Universal App, BTW). Here is the actual code in Ace Omicron for drawing a "menu" button in the upper left corner of the screen during the game, which when tapped will pause the game and bring up the main menu.

Code:
glPushMatrix();
        glLoadIdentity();
        
        // orient from center of screen
        glTranslatef(boundsWidth * 0.5f, boundsHeight * 0.5f, 0.0f);
        
        switch (orientation)
        {
            case UIInterfaceOrientationPortrait:
                break;
            case UIInterfaceOrientationPortraitUpsideDown:
                glRotatef(180.0f, 0.0f, 0.0f, 1.0f);
                break;
            case UIInterfaceOrientationLandscapeLeft:
                glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
                break;
            case UIInterfaceOrientationLandscapeRight:
                glRotatef(-90.0f, 0.0f, 0.0f, 1.0f);
                break;
        }
        
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glEnable(GL_TEXTURE_2D);
        
        if (layout == TQ_LAYOUT_IPHONE)
        {
            switch (orientation)
            {
                case UIInterfaceOrientationPortrait:
                    glTranslatef((-boundsWidth * 0.5f) + 64.0f, (boundsHeight * 0.5f) - 32.0f, 0.0f);
                    break;
                case UIInterfaceOrientationPortraitUpsideDown:
                    glTranslatef((-boundsWidth * 0.5f) + 64.0f, (boundsHeight * 0.5f) - 32.0f, 0.0f);
                    break;
                case UIInterfaceOrientationLandscapeLeft:
                    glTranslatef((-boundsHeight * 0.5f) + 64.0f, (boundsWidth * 0.5f) - 32.0f, 0.0f);
                    break;
                case UIInterfaceOrientationLandscapeRight:
                    glTranslatef((-boundsHeight * 0.5f) + 64.0f, (boundsWidth * 0.5f) - 32.0f, 0.0f);
                    break;
            }
        }
        else // (layout == TQ_LAYOUT_IPAD)
        {
            switch (orientation)
            {
                case UIInterfaceOrientationPortrait:
                    glTranslatef((-boundsWidth * 0.5f) + 128.0f, (boundsHeight * 0.5f) - 64.0f, 0.0f);
                    break;
                case UIInterfaceOrientationPortraitUpsideDown:
                    glTranslatef((-boundsWidth * 0.5f) + 128.0f, (boundsHeight * 0.5f) - 64.0f, 0.0f);
                    break;
                case UIInterfaceOrientationLandscapeLeft:
                    glTranslatef((-boundsHeight * 0.5f) + 128.0f, (boundsWidth * 0.5f) - 64.0f, 0.0f);
                    break;
                case UIInterfaceOrientationLandscapeRight:
                    glTranslatef((-boundsHeight * 0.5f) + 128.0f, (boundsWidth * 0.5f) - 64.0f, 0.0f);
                    break;
            }
        }
        
        glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
        [menuButtonTex drawAtPoint:CGPointMake(0.0f, 0.0f)];
        
    glPopMatrix();

Had to do that with every single HUD graphic. Pain. In. The. A$$. I have been working out a more automatic layout system since then.

[Edit]BTW, you might notice there is lots of symmetry in there since the menu button was in the upper left corner in all cases, so it seems like maybe it is over-dramatized, but that's only true for the menu button. It wasn't symmetrical for other HUD elements, which needed to be custom placed to fit just right, visually, depending on device/orientation. The menu button orientation was just a good template to work from.[/Edit]

Fortunately, the game graphics themselves are mostly 3D, so for those I was able to simply set the perspective projection to match the aspect for the most part.

One thing I realized while we were trying to figure out how to deal with the multiple orientations of iPad layout is that you want to use a 1024 x 1024 background. This way you can avoid having to letter-box/pillar-box the game. It's kind of the inverse of doing that, actually. Depending on the game, to make life easier, you might be able to have a square playing field that is 768 x 768, so that it doesn't matter which orientation the game is. The background is inverse of that and square too, so that it *always* covers the entire screen in any orientation. The HUD elements can always be stretched into position to fill out screen space appropriately (as in the example code above).

If this sounds confusing, then you understand the situation perfectly. Wink
Quote this message in a reply
Member
Posts: 129
Joined: 2009.03
Post: #17
Yup, confusing sounds about right!

I've been fidling with code, and set my EAGLView.contentScaleFactor to 2.0; and now everything is all pushed in to the lower right quadrant of the screen. No matter what I do with changes to the GL_PROJECTION matrix, it's always just using the lower right corner of the screen.

I must be missing something, somewhere, that's not to do with OpenGL, but is forcing the screen in to the lower right (lower right, inlandscape mode). I've not got any hi-res bitmaps in there yet.
Quote this message in a reply
Moderator
Posts: 3,570
Joined: 2003.06
Post: #18
Don't forget to set your glViewport to match the view.
Quote this message in a reply
Member
Posts: 129
Joined: 2009.03
Post: #19
I'm using code from the Xcode template project; so:

Code:
- (void)setFramebuffer
{
    if (context)
    {
        [EAGLContext setCurrentContext:context];

        if (!defaultFramebuffer)
            [self createFramebuffer];

        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);

        glViewport(0, 0, framebufferWidth, framebufferHeight);
    }
}

I just tried glViewport(0, 0, framebufferWidth/2, framebufferHeight/2); all the content is there, but it's still in the lower right corner, thus:

http://grab.by/7sPj
Quote this message in a reply
Moderator
Posts: 3,570
Joined: 2003.06
Post: #20
Did you make sure that the view is set to stretch to the window in the NIB?
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #21
Here's the code I use for this, in case it's of any use: http://sacredsoftware.net/svn/misc/StemL...EAGLView.m
Quote this message in a reply
Member
Posts: 129
Joined: 2009.03
Post: #22
I'm not using a NIB, using initWithCoder (I mean WithFrame).

I bet that's it. I noticed there was some stretch (contentStretch) property to the view, is that it?
Quote this message in a reply
Moderator
Posts: 3,570
Joined: 2003.06
Post: #23
initWithCoder is for initializing NIB instanced stuff. Wink Besides that, why not use a NIB?

I don't remember how to do it from code. Yes contentStretch sounds like it might be it. autoresizingMask also comes to mind.
Quote this message in a reply
Member
Posts: 129
Joined: 2009.03
Post: #24
(Nov 18, 2010 09:19 AM)ThemsAllTook Wrote:  Here's the code I use for this, in case it's of any use: http://sacredsoftware.net/svn/misc/StemL...EAGLView.m

Thanks.

Just spotted this, does your code never use displaylink?

Code:
displayLinkSupported = [[[UIDevice currentDevice] systemVersion] compare: @"3.1" options: NSNumericSearch] != NSOrderedAscending;
displayLinkSupported = NO;
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #25
(Nov 18, 2010 09:30 AM)Jamie W Wrote:  Just spotted this, does your code never use displaylink?

Code:
displayLinkSupported = [[[UIDevice currentDevice] systemVersion] compare: @"3.1" options: NSNumericSearch] != NSOrderedAscending;
displayLinkSupported = NO;

What the...? Must be test code I forgot to remove. Thanks for pointing it out!
Quote this message in a reply
Member
Posts: 129
Joined: 2009.03
Post: #26
@ThemsAllTook, I just mentioned it, incase it was something you forgot to comment out, and were using it in production code. Smile

I solved my own issue now, turns out I was doing something elsewhere with OpenGL scissors (clipping thingy), and it was just coded wrong. Tis all fixed now.

Now I need to focus on getting the PNG loading and scale thing working. After that, need to get the texture atlas tool creating @2x textures, and spitting out a data table that references a corresponding @1x texture.

I'm assuming I can use a mix of @2x and @1x textures, is that correct? Also, that @1x textures will render quite a bit faster than @2x textures?

Thanks once again for all your help and advice.
Quote this message in a reply
Moderator
Posts: 3,570
Joined: 2003.06
Post: #27
(Nov 18, 2010 01:36 PM)Jamie W Wrote:  I solved my own issue now, turns out I was doing something elsewhere with OpenGL scissors (clipping thingy), and it was just coded wrong. Tis all fixed now.

Aww man... I've been bitten by glScissor too (it's easy to forget about sometimes), so I know how it feels Rasp

(Nov 18, 2010 01:36 PM)Jamie W Wrote:  I'm assuming I can use a mix of @2x and @1x textures, is that correct?

Of course! There isn't anything special about @2x, other than it's two times the size and 4x the RAM. Otherwise, it's just another texture, perhaps with @2x in the name, which ultimately affects nothing other than your decision to load it or not.

(Nov 18, 2010 01:36 PM)Jamie W Wrote:  Also, that @1x textures will render quite a bit faster than @2x textures?

I am a little unclear about this myself, so if someone in the know could enlighten this subject, I'd appreciate it.

Here's how I understand it, but this might be entirely incorrect. The actual size of the texture means little in terms of performance, except for RAM usage. Fill-rate is what you are ultimately concerned about. That is, how fast the renderer can rasterize a given area of pixels; meaning that a 512x512 patch of pixels takes longer to "fill" than a 256x256 area of pixels. The renderer has to "read", that is, "sample" from a texture to determine what color to make a given fragment. So, if a texture is 512x512 and you're ultimately rendering to a 256x256 patch of pixels on-screen, then it needs to sample every other pixel in the texture. Again, in this case you're still ultimately "filling" 1/4 the number of pixels, even though the texture is 512x512. I am not sure if the fact that it needs to skip every other pixel during sampling negatively affects performance or not, or to what degree. I assume it doesn't affect performance much, but I could be dead wrong about that.

Conversely, if you're reading from a 256x256 texture and filling 512x512 pixels, I imagine it should take just about as long as reading from a 512x512 texture, filling a 512x512 patch. Right?
Quote this message in a reply
Member
Posts: 440
Joined: 2002.09
Post: #28
(Nov 18, 2010 01:36 PM)Jamie W Wrote:  I'm assuming I can use a mix of @2x and @1x textures, is that correct? Also, that @1x textures will render quite a bit faster than @2x textures?

Mixing @2x and @1x is fine where it makes sense. It may or may not be faster to render but it will cut down on memory bandwidth and usage. Cutting memory usage is probably the most direct benefit, unless you're also binding a whole lot of textures every frame.
Quote this message in a reply
Sage
Posts: 1,482
Joined: 2002.09
Post: #29
Reading from smaller textures does speed up rendering as it makes more efficient use of the texture cache and memory bandwidth, but it really only matters when you are scaling something down by more than ~2x I think. (I have no hard numbers to back this up so take it with a grain of salt) This is why mipmapping improves performance.

OT: ES supports scissoring... I thought it did not. Whoops. That would have cut down on fillrate even more on Twilight Golf.

Scott Lembcke - Howling Moon Software
Author of Chipmunk Physics - A fast and simple rigid body physics library in C.
Quote this message in a reply
Moderator
Posts: 770
Joined: 2003.04
Post: #30
Offtopic comment @AnotherJake:

Since you handle the "upside down" aspect with the rotate at the beginning of your code, couldn't you just do this?
Code:
    switch (orientation)
    {
        case UIInterfaceOrientationPortrait:
        case UIInterfaceOrientationPortraitUpsideDown:
            glTranslatef((-boundsWidth * 0.5f) + 64.0f, (boundsHeight * 0.5f) - 32.0f, 0.0f);
            break;
        case UIInterfaceOrientationLandscapeLeft:
        case UIInterfaceOrientationLandscapeRight:
            glTranslatef((-boundsHeight * 0.5f) + 64.0f, (boundsWidth * 0.5f) - 32.0f, 0.0f);
            break;
    }
    ...

Or even:
Code:
    bool portrait = (orientation == UIInterfaceOrientationPortrait || orientation == InterfaceOrientationPortraitUpsideDown);
    ...
    if (portrait)
        glTranslatef((-boundsHeight * 0.5f) + 64.0f, (boundsWidth * 0.5f) - 32.0f, 0.0f);
    else
        glTranslatef((-boundsHeight * 0.5f) + 64.0f, (boundsWidth * 0.5f) - 32.0f, 0.0f);
    ...
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Thoughts on this article? Elphaba 1 2,307 Jun 11, 2009 02:04 PM
Last Post: Bachus