Programming4us
         
 
 
Programming

iPhone 3D Programming : Vertices and Touch Points - Creating a Wireframe Viewer (part 3)

11/23/2010 11:57:12 AM

5. Poor Man’s Tab Bar

Apple provides the UITabBar widget as part of the UIKit framework. This is the familiar list of gray icons that many applications have along the bottom of the screen, as shown in Figure 4.

Figure 4. UITabBar


Since UIKit widgets are outside the scope of this book, you’ll be using OpenGL to create a poor man’s tab bar for switching between the various parametric surfaces, as in Figure 5.

Figure 5. Poor man’s tab bar


In many situations like this, a standard UITabBar is preferable since it creates a more consistent look with other iPhone applications. But in our case, we’ll create a fun transition effect: pushing a button will cause it to “slide out” of the tab bar and into the main viewport. For this level of control over rendering, UIKit doesn’t suffice.

The wireframe viewer has a total of six parametric surfaces, but the button bar has only five. When the user touches a button, we’ll swap its contents with the surface being displayed in the main viewport. This allows the application to support six surfaces with only five buttons.

The state for the five buttons and the button-detection code lives in the application engine. New lines in the class declaration from ApplicationEngine.cpp are shown in bold in Example 7. No modifications to the two rendering engines are required.

Example 7. ApplicationEngine declaration with tab bar
#include "ParametricEquations.hpp"
#include <algorithm>

using namespace std;

static const int SurfaceCount = 6;
static const int ButtonCount = SurfaceCount - 1;

class ApplicationEngine : public IApplicationEngine {
public:
ApplicationEngine(IRenderingEngine* renderingEngine);
~ApplicationEngine();
void Initialize(int width, int height);
void OnFingerUp(ivec2 location);
void OnFingerDown(ivec2 location);
void OnFingerMove(ivec2 oldLocation, ivec2 newLocation);
void Render() const;
void UpdateAnimation(float dt);
private:
void PopulateVisuals(Visual* visuals) const;
int MapToButton(ivec2 touchpoint) const;
vec3 MapToSphere(ivec2 touchpoint) const;
float m_trackballRadius;
ivec2 m_screenSize;
ivec2 m_centerPoint;
ivec2 m_fingerStart;
bool m_spinning;
Quaternion m_orientation;
Quaternion m_previousOrientation;
IRenderingEngine* m_renderingEngine;
int m_currentSurface;
ivec2 m_buttonSize;
int m_pressedButton;
int m_buttonSurfaces[ButtonCount];
};


Example 8 shows the implementation. Methods left unchanged (such as MapToSphere) are omitted for brevity. You’ll be replacing the following methods: ApplicationEngine::ApplicationEngine, Initialize, Render, OnFingerUp, OnFingerDown, and OnFingerMove. There are two new methods you’ll be adding: ApplicationEngine::PopulateVisuals and MapToButton.

Example 8. ApplicationEngine implementation with tab bar
ApplicationEngine::ApplicationEngine(IRenderingEngine* renderingEngine) :
m_spinning(false),
m_renderingEngine(renderingEngine),
m_pressedButton(-1)
{
m_buttonSurfaces[0] = 0;
m_buttonSurfaces[1] = 1;
m_buttonSurfaces[2] = 2;
m_buttonSurfaces[3] = 4;
m_buttonSurfaces[4] = 5;
m_currentSurface = 3;
}

void ApplicationEngine::Initialize(int width, int height)
{
m_trackballRadius = width / 3;
m_buttonSize.y = height / 10;
m_buttonSize.x = 4 * m_buttonSize.y / 3;
m_screenSize = ivec2(width, height - m_buttonSize.y);
m_centerPoint = m_screenSize / 2;

vector<ISurface*> surfaces(SurfaceCount);
surfaces[0] = new Cone(3, 1);
surfaces[1] = new Sphere(1.4f);
surfaces[2] = new Torus(1.4f, 0.3f);
surfaces[3] = new TrefoilKnot(1.8f);
surfaces[4] = new KleinBottle(0.2f);
surfaces[5] = new MobiusStrip(1);
m_renderingEngine->Initialize(surfaces);
for (int i = 0; i < SurfaceCount; i++)
delete surfaces[i];
}


void ApplicationEngine::PopulateVisuals(Visual* visuals) const
{
for (int buttonIndex = 0; buttonIndex < ButtonCount; buttonIndex++) {
int visualIndex = m_buttonSurfaces[buttonIndex];
visuals[visualIndex].Color = vec3(0.75f, 0.75f, 0.75f);
if (m_pressedButton == buttonIndex)
visuals[visualIndex].Color = vec3(1, 1, 1);

visuals[visualIndex].ViewportSize = m_buttonSize;
visuals[visualIndex].LowerLeft.x = buttonIndex * m_buttonSize.x;
visuals[visualIndex].LowerLeft.y = 0;
visuals[visualIndex].Orientation = Quaternion();
}

visuals[m_currentSurface].Color = m_spinning ? vec3(1, 1, 1) : vec3(0, 1, 1);
visuals[m_currentSurface].LowerLeft = ivec2(0, 48);
visuals[m_currentSurface].ViewportSize = ivec2(320, 432);
visuals[m_currentSurface].Orientation = m_orientation;
}

void ApplicationEngine::Render() const
{
vector<Visual> visuals(SurfaceCount);
PopulateVisuals(&visuals[0]);
m_renderingEngine->Render(visuals);
}

void ApplicationEngine::OnFingerUp(ivec2 location)
{
m_spinning = false;

if (m_pressedButton != -1 && m_pressedButton == MapToButton(location))
swap(m_buttonSurfaces[m_pressedButton], m_currentSurface);

m_pressedButton = -1;
}

void ApplicationEngine::OnFingerDown(ivec2 location)
{
m_fingerStart = location;
m_previousOrientation = m_orientation;
m_pressedButton = MapToButton(location);
if (m_pressedButton == -1)
m_spinning = true;
}

void ApplicationEngine::OnFingerMove(ivec2 oldLocation, ivec2 location)
{
if (m_spinning) {
vec3 start = MapToSphere(m_fingerStart);
vec3 end = MapToSphere(location);
Quaternion delta = Quaternion::CreateFromVectors(start, end);
m_orientation = delta.Rotated(m_previousOrientation);
}

if (m_pressedButton != -1 && m_pressedButton != MapToButton(location))
m_pressedButton = -1;
}

int ApplicationEngine::MapToButton(ivec2 touchpoint) const
{
if (touchpoint.y < m_screenSize.y - m_buttonSize.y)
return -1;

int buttonIndex = touchpoint.x / m_buttonSize.x;
if (buttonIndex >= ButtonCount)
return -1;

return buttonIndex;
}



Go ahead and try it—at this point, the wireframe viewer is starting to feel like a real application!

6. Animating the Transition

The button-swapping strategy is clever but possibly jarring to users; after playing with the app for a while, the user might start to notice that his tab bar is slowly being re-arranged. To make the swap effect more obvious and to give the app more of a fun Apple feel, let’s create a transition animation that actually shows the button being swapped with the main viewport. Figure 6 depicts this animation.

Figure 6. Transition animation in wireframe viewer


Again, no changes to the two rendering engines are required, because all the logic can be constrained to ApplicationEngine. In addition to animating the viewport, we’ll also animate the color (the tab bar wireframes are drab gray) and the orientation (the tab bar wireframes are all in the “home” position). We can reuse the existing Visual class for this; we need two sets of Visual objects for the start and end of the animation. While the animation is active, we’ll tween the values between the starting and ending visuals. Let’s also create an Animation structure to bundle the visuals with a few other animation parameters, as shown in bold in Example 9.

Example 9. ApplicationEngine declaration with transition animation
struct Animation {
bool Active;
float Elapsed;
float Duration;
Visual StartingVisuals[SurfaceCount];
Visual EndingVisuals[SurfaceCount];
};

class ApplicationEngine : public IApplicationEngine {
public:
// ...
private:
// ...
Animation m_animation;
};

Example 10 shows the new implementation of ApplicationEngine. Unchanged methods are omitted for brevity. Remember, animation is all about interpolation! The Render method leverages the Lerp and Slerp methods from our vector class library to achieve the animation in a surprisingly straightforward manner.

Example 10. ApplicationEngine implementation with transition animation
ApplicationEngine::ApplicationEngine(IRenderingEngine* renderingEngine) :
m_spinning(false),
m_renderingEngine(renderingEngine),
m_pressedButton(-1)
{
m_animation.Active = false;

// Same as in Example 3-17
....
}

void ApplicationEngine::Render() const
{
vector<Visual> visuals(SurfaceCount);

if (!m_animation.Active) {
PopulateVisuals(&visuals[0]);
} else {
float t = m_animation.Elapsed / m_animation.Duration;
for (int i = 0; i < SurfaceCount; i++) {
const Visual& start = m_animation.StartingVisuals[i];
const Visual& end = m_animation.EndingVisuals[i];
Visual& tweened = visuals[i];

tweened.Color = start.Color.Lerp(t, end.Color);
tweened.LowerLeft = start.LowerLeft.Lerp(t, end.LowerLeft);
tweened.ViewportSize = start.ViewportSize.Lerp(t, end.ViewportSize);
tweened.Orientation = start.Orientation.Slerp(t, end.Orientation);
}
}

m_renderingEngine->Render(visuals);
}

void ApplicationEngine::UpdateAnimation(float dt)
{
if (m_animation.Active) {
m_animation.Elapsed += dt;
if (m_animation.Elapsed > m_animation.Duration)
m_animation.Active = false;
}
}

void ApplicationEngine::OnFingerUp(ivec2 location)
{
m_spinning = false;

if (m_pressedButton != -1 && m_pressedButton == MapToButton(location) &&
!m_animation.Active)
{
m_animation.Active = true;
m_animation.Elapsed = 0;
m_animation.Duration = 0.25f;

PopulateVisuals(&m_animation.StartingVisuals[0]);
swap(m_buttonSurfaces[m_pressedButton], m_currentSurface);
PopulateVisuals(&m_animation.EndingVisuals[0]);
}

m_pressedButton = -1;
}



That completes the wireframe viewer! As you can see, animation isn’t difficult, and it can give your application that special Apple touch.

Other -----------------
- iPhone 3D Programming : Vertices and Touch Points - Boosting Performance with Vertex Buffer Objects
- iPhone 3D Programming : Vertices and Touch Points - Saving Memory with Vertex Indexing
- iPhone 3D Programming : Vertices and Touch Points - Reading the Touchscreen
- iPhone 3D Programming : HelloCone with Shaders
- Search Engine Basics : Country-Specific Search Engines
- Search Engine Basics : Vertical Search Engines
- Building Android Apps : Animation - Adding the Settings Panel
- Building Android Apps : Animation - Adding the New Entry Panel
- Building Android Apps : Animation - Adding the Date Panel
- Building Android Apps : Animation - Adding the Dates Panel
- Building Android Apps : Animation - Sliding Home
- Programming Windows Azure : Understanding the Value of Queues
- Programming Windows Azure : Table Operations - Deleting Tables, Deleting Entities
- Programming Windows Azure : Table Operations - Updating Entities
- Programming Windows Azure : Table Operations - Understanding Pagination
- Programming Windows Azure : Table Operations - Using Partitioning
- Programming Windows Azure : Table Operations - Querying Data
- Programming Windows Azure : Table Operations - Creating Entities
- Programming Windows Azure : Table Operations - Creating Tables
- iPad Development : Document Management (part 2)
 
 
Most View
- Keyword Research Tools (part 2)
- SharePoint Server 2010 Business Intelligence Platform (part 3) - PerformancePoint Services - Create a Dashboard
- Windows 7 : Setting Up the Remote Computer as a Host (part 2) - Configuring XP to Act as a Remote Desktop Host
- SharePoint 2010 : Search for Documents and List Items
- Installing SQL Server 2012 : The Installation Process (part 3) - Installing SQL Server 2012 Through the Command Line, Installing SQL Server 2012 Through PowerShell
- SharePoint 2010 : Configuring Service Applications (part 3) - Modifying the Application Pool of a Deployed Service Application
- Exchange Server 2003 : Creating and Managing Address Lists and Recipient Policies (part 1) - Creating and Modifying Address Lists
- Windows 7 : Protecting Yourself Against Email Viruses
- Windows Firewall with Advanced Security in Windows Server 2008 (part 1)
- Unit Testing in Visual Studio 2010 (part 1) - Creating unit tests
Top 10
- Implementing Edge Services for an Exchange Server 2007 Environment : Utilizing the Basic Sender and Recipient Connection Filters (part 3) - Configuring Recipient Filtering
- Implementing Edge Services for an Exchange Server 2007 Environment : Utilizing the Basic Sender and Recipient Connection Filters (part 2)
- Implementing Edge Services for an Exchange Server 2007 Environment : Utilizing the Basic Sender and Recipient Connection Filters (part 1)
- Implementing Edge Services for an Exchange Server 2007 Environment : Installing and Configuring the Edge Transport Server Components
- What's New in SharePoint 2013 (part 7) - BCS
- What's New in SharePoint 2013 (part 6) - SEARCH
- What's New in SharePoint 2013 (part 6) - WEB CONTENT MANAGEMENT
- What's New in SharePoint 2013 (part 5) - ENTERPRISE CONTENT MANAGEMENT
- What's New in SharePoint 2013 (part 4) - WORKFLOWS
- What's New in SharePoint 2013 (part 3) - REMOTE EVENTS