MULTIMEDIA

iPhone 3D Programming : Holodeck Sample (part 3) - Handling the Heads-Up Display

3/25/2011 9:08:13 AM

3. Handling the Heads-Up Display

Most applications that need to render a HUD take the following approach when rendering a single frame of animation:

  1. Issue a glClear.

  2. Set up the model-view and projection matrices for the 3D scene.

  3. Render the 3D scene.

  4. Disable depth testing, and enable blending.

  5. Set up the model-view and projection matrices for 2D rendering.

  6. Render the HUD.


Warning:

Always remember to completely reset your transforms at the beginning of the render routine; otherwise, you’ll apply transformations that are left over from the previous frame. For example, calling glFrustum alone simply multiplies the current matrix, so you might need to issue a glLoadIdentity immediately before calling glFrustum.


Let’s go ahead and modify the Render method to render buttons; replace the ellipses in Example 5 with the code in Example 7.

Example 7. Adding buttons to Holodeck
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, m_textures.Button);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrthof(-160, 160, -240, 240, 0, 1);

if (buttons & ButtonFlagsShowHorizontal) {
glMatrixMode(GL_MODELVIEW);
glTranslatef(200, 0, 0);
SetButtonAlpha(buttons, ButtonFlagsPressingLeft);
RenderDrawable(m_drawables.Quad);
glTranslatef(-400, 0, 0);
glMatrixMode(GL_TEXTURE);
glRotatef(180, 0, 0, 1);
SetButtonAlpha(buttons, ButtonFlagsPressingRight);
RenderDrawable(m_drawables.Quad);
glRotatef(-180, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glTranslatef(200, 0, 0);
}

if (buttons & ButtonFlagsShowVertical) {
glMatrixMode(GL_MODELVIEW);
glTranslatef(0, 125, 0);
glMatrixMode(GL_TEXTURE);
glRotatef(90, 0, 0, 1);
SetButtonAlpha(buttons, ButtonFlagsPressingUp);
RenderDrawable(m_drawables.Quad);
glMatrixMode(GL_MODELVIEW);
glTranslatef(0, -250, 0);
glMatrixMode(GL_TEXTURE);
glRotatef(180, 0, 0, 1);
SetButtonAlpha(buttons, ButtonFlagsPressingDown);
RenderDrawable(m_drawables.Quad);
glRotatef(90, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glTranslatef(0, 125, 0);
}



glColor4f(1, 1, 1, 1);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);


Note that Example 7 contains quite a few transform operations; while this is fine for teaching purposes, in a production environment I recommend including all four buttons in a single VBO. You’d still need four separate draw calls, however, since the currently pressed button has a unique alpha value.

In fact, making this optimization would be an interesting project: create a single VBO that contains all four pretransformed buttons, and then render it with four separate draw calls. Don’t forget that the second argument to glDrawArrays can be nonzero!

The SetButtonAlpha method sets alpha to one if the button is being pressed; otherwise, it makes the button semitransparent:

void RenderingEngine::SetButtonAlpha(ButtonMask buttonFlags, 
ButtonFlags flag) const
{
float alpha = (buttonFlags & flag) ? 1.0 : 0.75;
glColor4f(1, 1, 1, alpha);
}

Next let’s go over the code in GLView.mm that detects button presses and maintains the azimuth/altitude angles. See Example 8 for the GLView class declaration and Example 9 for the interesting potions of the class implementation.

Example 8. GLView.h for Holodeck
#import "Interfaces.hpp"
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import <CoreLocation/CoreLocation.h>

@interface GLView : UIView {
@private
IRenderingEngine* m_renderingEngine;
IResourceManager* m_resourceManager;
EAGLContext* m_context;
bool m_paused;
float m_theta;
float m_phi;
vec2 m_velocity;
ButtonMask m_visibleButtons;
float m_timestamp;
}

- (void) drawView: (CADisplayLink*) displayLink;

@end

Example 9. GLView.mm for Holodeck
...


- (id) initWithFrame: (CGRect) frame
{
m_paused = false;
m_theta = 0;
m_phi = 0;
m_velocity = vec2(0, 0);
m_visibleButtons = ButtonFlagsShowHorizontal | ButtonFlagsShowVertical;

if (self = [super initWithFrame:frame]) {
CAEAGLLayer* eaglLayer = (CAEAGLLayer*) self.layer;
eaglLayer.opaque = YES;

EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES1;
m_context = [[EAGLContext alloc] initWithAPI:api];

if (!m_context || ![EAGLContext setCurrentContext:m_context]) {
[self release];
return nil;
}

m_resourceManager = CreateResourceManager();

NSLog(@"Using OpenGL ES 1.1");
m_renderingEngine = CreateRenderingEngine(m_resourceManager);

[m_context
renderbufferStorage:GL_RENDERBUFFER
fromDrawable: eaglLayer];

m_timestamp = CACurrentMediaTime();

m_renderingEngine->Initialize();
[self drawView:nil];

CADisplayLink* displayLink;
displayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(drawView:)];

[displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
}
return self;
}

- (void) drawView: (CADisplayLink*) displayLink
{
if (m_paused)
return;

if (displayLink != nil) {
const float speed = 30;
float elapsedSeconds = displayLink.timestamp - m_timestamp;
m_timestamp = displayLink.timestamp;
m_theta -= speed * elapsedSeconds * m_velocity.x;
m_phi += speed * elapsedSeconds * m_velocity.y;
}

ButtonMask buttonFlags = m_visibleButtons;
if (m_velocity.x < 0) buttonFlags |= ButtonFlagsPressingLeft;
if (m_velocity.x > 0) buttonFlags |= ButtonFlagsPressingRight;
if (m_velocity.y < 0) buttonFlags |= ButtonFlagsPressingUp;
if (m_velocity.y > 0) buttonFlags |= ButtonFlagsPressingDown;

m_renderingEngine->Render(m_theta, m_phi, buttonFlags);
[m_context presentRenderbuffer:GL_RENDERBUFFER];
}

bool buttonHit(CGPoint location, int x, int y)
{
float extent = 32;
return (location.x > x - extent && location.x < x + extent &&
location.y > y - extent && location.y < y + extent);
}

- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event
{
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInView: self];
float delta = 1;

if (m_visibleButtons & ButtonFlagsShowVertical) {
if (buttonHit(location, 35, 240))
m_velocity.y = -delta;
else if (buttonHit(location, 285, 240))
m_velocity.y = delta;
}

if (m_visibleButtons & ButtonFlagsShowHorizontal) {
if (buttonHit(location, 160, 40))
m_velocity.x = -delta;
else if (buttonHit(location, 160, 440))
m_velocity.x = delta;
}
}

- (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event
{
m_velocity = vec2(0, 0);
}


At this point, you now have a complete app that lets you look around inside a (rather boring) virtual world, but it’s still a far cry from augmented reality!
Other  
  •  Building LOB Applications : Printing in a Silverlight LOB Application
  •  Building LOB Applications : Data Validation through Data Annotation
  •  Building LOB Applications : Implementing CRUD Operations in RIA Services
  •  Microsoft XNA Game Studio 3.0 : Displaying Images - Resources and Content (part 2) - Adding Resources to a Project
  •  Microsoft XNA Game Studio 3.0 : Displaying Images - Resources and Content (part 1)
  •  iPhone 3D Programming : Blending and Augmented Reality - Rendering Anti-Aliased Lines with Textures
  •  Programming with DirectX : Game Math - Bounding Geometry (part 2) - Bounding Spheres & Bounding Hierarchies
  •  Programming with DirectX : Game Math - Bounding Geometry (part 1) - Bounding Boxes
  •  Programming with DirectX : Game Math - Matrices
  •  iPhone 3D Programming : Anti-Aliasing Tricks with Offscreen FBOs (part 2) - Jittering
  •  iPhone 3D Programming : Anti-Aliasing Tricks with Offscreen FBOs (part 1) - A Super Simple Sample App for Supersampling
  •  Building LOB Applications : Navigating RIA LOB Data
  •  Building LOB Applications : Databinding in XAML
  •  Microsoft XNA Game Studio 3.0 : Program Bugs
  •  Microsoft XNA Game Studio 3.0 : Getting Player Input - Adding Vibration
  •  Microsoft XNA Game Studio 3.0 : Getting Player Input - Using the Keyboard
  •  iPhone 3D Programming : Blending and Augmented Reality - Stencil Alternatives for Older iPhones
  •  iPhone 3D Programming : Blending and Augmented Reality - Poor Man’s Reflection with the Stencil Buffer
  •  Microsoft XNA Game Studio 3.0 : Getting Player Input - Reading a Gamepad
  •  iPhone 3D Programming : Blending and Augmented Reality - Shifting Texture Color with Per-Vertex Color
  •  
    Most View
    Guide To Upgrades With The Greatest Effects (Part 1)
    Cutting Edge Technology (Part 2)
    Tried And Tested – November 2012 (Part 1)
    The KDE User Guide (Part 2)
    Hard Disk Help: From Unusual Noises To Random Crashes
    Asus GeForce GTX 690 - SLI speeds on a single card
    NZXT Kraken X60 - The Best Liquid Cooling System (Part 1)
    Gigabyte GA-F2A85X-UP4 Mainboard & AMD A10-5800K Processor Review (Part 7)
    The Cure Of Slightly Rubbish Technology (Part 2)
    Which Is The Real Best-Seller Ultrabook? (Part 1) - Asus Zenbook Prime, Acer Aspire S5, HP Folio 13-2000
    Top 10
    Does Microsoft Have An Image Problem? (Part 2)
    Does Microsoft Have An Image Problem? (Part 1)
    Time For A Bigger iPhone?
    99 Mac Secrets (Part 5) - Top ten third-party apps
    99 Mac Secrets (Part 4) - iMovie secrets, GarageBand secrets, iWork secrets
    99 Mac Secrets (Part 3) : Safari secrets, Mail secrets, Safari shortcuts, Mail shortcuts, iPhoto secrets
    99 Mac Secrets (Part 2) : Customizing, Best menu bar add-ons, Quick Look secrets
    99 Mac Secrets (Part 1) : General OS X tips, Security tips, System shortcuts
    iMovie Trailers And Audio Premastered
    PowerTraveller Powerchimp 4A Battery Charger