diff --git a/.gitignore b/.gitignore
index e11069d..22fc997 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@
 
 # Project Specific.
 *Lephisto3D*
+*ModelViewer*
 bin/*
 *Makefile
 *Makefile.in
diff --git a/src/Makefile.am b/src/Makefile.am
index 61058a4..4af40fd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,15 +1,8 @@
 ## Process this file with automake to produce Makefile.in
 SUBDIRS = sbre/
 
-bin_PROGRAMS		= Lephisto3D
-Lephisto3D_SOURCES	= main.cpp gui_button.cpp gui.cpp gui_fixed.cpp gui_screen.cpp gui_label.cpp glfreetype.cpp \
-		body.cpp space.cpp ship.cpp player.cpp gui_toggle_button.cpp gui_radio_button.cpp \
-		gui_radio_group.cpp dynamic_body.cpp planet.cpp star.cpp frame.cpp gui_image_button.cpp gui_image.cpp \
-		gui_image_radio_button.cpp gui_multi_state_image_button.cpp ship_cpanel.cpp gui_widget.cpp sector_view.cpp \
-		mtrand.cpp world_view.cpp system_view.cpp star_system.cpp sector.cpp system_info_view.cpp generic_system_view.cpp \
-		gui_container.cpp date.cpp space_station.cpp space_station_view.cpp model_body.cpp ship_type.cpp \
-    info_view.cpp model_coll_mesh_data.cpp object_viewer_view.cpp custom_starsystems.cpp
-Lephisto3D_LDADD = sbre/libsbre.a
+bin_PROGRAMS		= Lephisto3D ModelViewer
+noinst_LIBRARIES = libgui.a
 
 include_HEADERS = body.h frame.h generic_system_view.h glfreetype.h gui_button.h gui_container.h gui_events.h gui_fixed.h \
 		gui.h gui_image_button.h gui_image.h gui_image_radio_button.h gui_label.h gui_multi_state_image_button.h gui_radio_button.h \
@@ -18,3 +11,18 @@ include_HEADERS = body.h frame.h generic_system_view.h glfreetype.h gui_button.h
 		system_view.h vector3.h view.h world_view.h date.h space_station.h space_station_view.h model_body.h gui_iselectable.h \
 		ship_type.h object.h info_view.h model_coll_mesh_data.h object_viewer_view.h fixed.h custom_starsystems.h
 
+libgui_a_SOURCES = gui_button.cpp gui.cpp gui_fixed.cpp gui_screen.cpp gui_label.cpp gui_toggle_button.cpp gui_radio_button.cpp \
+									 gui_radio_group.cpp gui_image_button.cpp gui_image.cpp gui_image_radio_button.cpp gui_multi_state_image_button.cpp gui_widget.cpp \
+									 gui_container.cpp
+
+Lephisto3D_SOURCES	= main.cpp glfreetype.cpp body.cpp space.cpp ship.cpp player.cpp dynamic_body.cpp planet.cpp \
+											star.cpp frame.cpp ship_cpanel.cpp sector_view.cpp mtrand.cpp world_view.cpp system_view.cpp \
+											star_system.cpp sector.cpp system_info_view.cpp generic_system_view.cpp date.cpp space_station.cpp \
+											space_station_view.cpp model_body.cpp ship_type.cpp info_view.cpp model_coll_mesh_data.cpp \
+											object_viewer_view.cpp custom_starsystems.cpp
+Lephisto3D_LDADD = sbre/libsbre.a libgui.a
+
+ModelViewer_SOURCES = sbre_viewer.cpp glfreetype.cpp
+ModelViewer_LDADD = sbre/libsbre.a libgui.a
+
+
diff --git a/src/body.cpp b/src/body.cpp
index 7149f1d..cd3f3ef 100644
--- a/src/body.cpp
+++ b/src/body.cpp
@@ -27,3 +27,18 @@ const vector3d& Body::GetProjectedPos(void) const {
   return m_projectedPos;
 }
 
+void Body::OrientOnSurface(double radius, double latitude, double longitude) {
+  vector3d pos = vector3d(radius*cos(latitude)*cos(longitude),
+      radius*sin(latitude)*cos(longitude), radius*sin(longitude));
+  vector3d up = vector3d::Normalize(pos);
+  SetPosition(pos);
+
+  vector3d forward = vector3d(0,0,1);
+  vector3d other = vector3d::Normalize(vector3d::Cross(up, forward));
+  forward = vector3d::Cross(other, up);
+
+  matrix4x4d rot = matrix4x4d::MakeRotMatrix(other, up, forward);
+  rot = rot.InverseOf();
+  SetRotMatrix(rot);
+}
+
diff --git a/src/body.h b/src/body.h
index 6b68b6a..dd5c51b 100644
--- a/src/body.h
+++ b/src/body.h
@@ -29,6 +29,8 @@ public:
   /* Override to clear any pointers you hold to the dying body. */
   virtual void NotifyDeath(const Body* const dyingBody) {}
 
+  /* For putting on planet surface, oriented +y up. */
+  void OrientOnSurface(double radius, double latitude, double longitude);
   vector3d GetPositionRelTo(const Frame*);
   Frame* GetFrame(void) { return m_frame; }
   void SetLabel(const char* label) { m_label = label; }
diff --git a/src/glfreetype.cpp b/src/glfreetype.cpp
index cb0b556..111f35c 100644
--- a/src/glfreetype.cpp
+++ b/src/glfreetype.cpp
@@ -303,7 +303,7 @@ void FontFace::RenderString(const char* str) {
 void FontFace::RenderMarkup(const char* str) {
   glPushMatrix();
   int len = strlen(str);
-  for(unsigned int i = 0; i < len; i++) {
+  for(int i = 0; i < len; i++) {
     if(str[i] == '#') {
       int hexcol;
       if(sscanf(str+i, "#%3x", &hexcol)==1) {
@@ -331,8 +331,9 @@ void FontFace::RenderMarkup(const char* str) {
 
 FontFace::FontFace(const char* filename_ttf) {
   FT_Face face;
-  if(0 != FT_New_Face(library, filename_ttf, 0, &face)) {
-    fprintf(stderr, "Error: Couldn't load '%s'\n", filename_ttf);
+  int err;
+  if(0 != (err = FT_New_Face(library, filename_ttf, 0, &face))) {
+    fprintf(stderr, "Terrible error! Couldn't load '%s'; error %d.\n", filename_ttf, err);
   } else {
     FT_Set_Char_Size(face, 50*64, 0, 100, 0);
     for(int chr = 32; chr < 127; chr++) {
diff --git a/src/gui_fixed.cpp b/src/gui_fixed.cpp
index 0805e1c..58e0cc0 100644
--- a/src/gui_fixed.cpp
+++ b/src/gui_fixed.cpp
@@ -1,6 +1,5 @@
 #include "libs.h"
 #include "gui.h"
-#include "l3d.h"
 
 namespace Gui {
 
diff --git a/src/gui_image.cpp b/src/gui_image.cpp
index 9be92cd..84aa81e 100644
--- a/src/gui_image.cpp
+++ b/src/gui_image.cpp
@@ -1,6 +1,5 @@
 #include "libs.h"
 #include "gui_image.h"
-#include "l3d.h"
 
 namespace Gui {
 
@@ -12,7 +11,7 @@ Image::Image(const char* filename) : Widget() {
   SDL_Surface* is = IMG_Load(filename);
   if(!is) {
     fprintf(stderr, "Could not load %s\n", filename);
-    L3D::Quit();
+    exit(0);
   }
   m_imgw = is->w;
   m_imgh = is->h;
diff --git a/src/gui_image_button.cpp b/src/gui_image_button.cpp
index 652d0e5..42f4ba7 100644
--- a/src/gui_image_button.cpp
+++ b/src/gui_image_button.cpp
@@ -1,7 +1,6 @@
 #include "libs.h"
 #include "gui.h"
 #include "gui_image_button.h"
-#include "l3d.h"
 
 namespace Gui {
 
diff --git a/src/gui_image_radio_button.cpp b/src/gui_image_radio_button.cpp
index 83c5387..43ad2a5 100644
--- a/src/gui_image_radio_button.cpp
+++ b/src/gui_image_radio_button.cpp
@@ -1,7 +1,6 @@
 #include "libs.h"
 #include "gui.h"
 #include "gui_image_radio_button.h"
-#include "l3d.h"
 
 namespace Gui {
 
diff --git a/src/gui_toggle_button.cpp b/src/gui_toggle_button.cpp
index 5c6a009..2903113 100644
--- a/src/gui_toggle_button.cpp
+++ b/src/gui_toggle_button.cpp
@@ -15,17 +15,26 @@ bool ToggleButton::OnMouseDown(MouseButtonEvent* e) {
     onPress.emit();
     m_pressed = !m_pressed;
     if(m_pressed) {
-      onSelect.emit(this);
+      onChange.emit(this, true);
     } else {
-      onDeselect.emit(this);
+      onChange.emit(this, false);
     }
   }
   return false;
 }
 
-void ToggleButton::GetSizeRequested(float& w, float& h) {
-  w = BUTTON_SIZE;
-  h = BUTTON_SIZE;
+void ToggleButton::OnActivate(void) {
+  m_pressed = !m_pressed;
+  if(m_pressed) {
+    onChange.emit(this, true);
+  } else {
+    onChange.emit(this, false);
+  }
+}
+
+void ToggleButton::GetSizeRequested(float size[2]) {
+  size[0] = BUTTON_SIZE;
+  size[1] = BUTTON_SIZE;
 }
 
 void ToggleButton::Draw(void) {
diff --git a/src/gui_toggle_button.h b/src/gui_toggle_button.h
index a5ca6e9..4f152b1 100644
--- a/src/gui_toggle_button.h
+++ b/src/gui_toggle_button.h
@@ -8,13 +8,13 @@ namespace Gui {
     ToggleButton(void);
     virtual void Draw(void);
     virtual ~ToggleButton(void) {}
-    virtual void GetSizeRequested(float& w, float& h);
+    virtual void GetSizeRequested(float size[2]);
     virtual bool OnMouseDown(MouseButtonEvent* e);
+    virtual void OnActivate(void);
     void SetPressed(bool s) { m_pressed = s; }
     bool GetPressed(void)   { return m_pressed; }
 
-    sigc::signal<void, ToggleButton*> onSelect;
-    sigc::signal<void, ToggleButton*> onDeselect;
+    sigc::signal<void, ToggleButton*, bool> onChange;
   private:
     int m_pressed;
   };
diff --git a/src/main.cpp b/src/main.cpp
index 30859d1..6a36d0c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -233,13 +233,20 @@ void L3D::MainLoop(void) {
   station->SetPosition(vector3d(0,0,0));
   Space::AddBody(station);
 
-  //player->SetFrame(stationFrame);
+  SpaceStation* station2 = new SpaceStation();
+  station2->SetLabel("Dfighter's point");
+  station2->SetFrame(*pframe->m_children.begin()); /* Rotating frame of planet. */
+  station2->OrientOnSurface(EARTH_RADIUS, M_PI/4, M_PI/4);
+  Space::AddBody(station2);
+
+  player->SetFrame(stationFrame);
+  player->SetPosition(vector3d(0,0,0));
   //player->SetPosition(vector3d(0,0,2000));
-  player->SetFrame(pframe);
+  //player->SetFrame(pframe);
   float ang1 = L3D::rng.Double(0,M_PI);
   float ang2 = L3D::rng.Double(0,M_PI);
   double r = EARTH_RADIUS*1.0001;
-  player->SetPosition(vector3d(r*cos(ang1)*cos(ang2), r*sin(ang1)*cos(ang2), r*sin(ang2)));
+  //player->SetPosition(vector3d(r*cos(ang1)*cos(ang2), r*sin(ang1)*cos(ang2), r*sin(ang2)));
   //player->SetPosition(vector3d(r, 0, 0);
 
   Gui::Init(scrWidth, scrHeight, 640, 480);
@@ -256,7 +263,7 @@ void L3D::MainLoop(void) {
   infoView          = new InfoView();
 
   SetView(worldView);
-  //player->SetDockedWith(station);
+  player->SetDockedWith(station);
 
   Uint32 last_stats = SDL_GetTicks();
   int frame_stat = 0;
diff --git a/src/sbre/models.cpp b/src/sbre/models.cpp
index 0bb71a3..9d68bff 100644
--- a/src/sbre/models.cpp
+++ b/src/sbre/models.cpp
@@ -5,6 +5,10 @@ enum AxisIndex {
   A_X = 0, A_Y, A_Z, A_NX, A_NY, A_NZ,
 };
 
+static CompoundVertex dummyvtx2[] = {
+  { VTYPE_CROSS, { 0, 1, 2, static_cast<uint16>(-1), static_cast<uint16>(-1) } }, /* Dummy. */
+};
+
 static PlainVertex tetravtx1[] = {
   { VTYPE_PLAIN, { 0.0f, 50.0f, 0.0f } },     /* 6. */
   { VTYPE_PLAIN, { -50.0f, -30.0f, 30.0f } },
@@ -1241,3 +1245,65 @@ static Thruster wing2thruster[] = {
 Model wing2model = { 1.0f, 25.0f, 23, wing2vtx1, 30, 0, wing2vtx2, 2,
                    { { 0, wing2data, 0, 2, wing2thruster } } };
 
+static PlainVertex metalFrameTowerVtx1[] = {
+  { VTYPE_PLAIN, { -1, 0, 1 } },
+  { VTYPE_PLAIN, { 1, 0, 1 } },
+  { VTYPE_PLAIN, { 1, 0, -1 } },
+  { VTYPE_PLAIN, { -1, 0, -1 } },
+  { VTYPE_PLAIN, { -1, 10, 1 } },
+  { VTYPE_PLAIN, { 1, 10, 1 } },
+  { VTYPE_PLAIN, { 1, 10, -1 } },
+  { VTYPE_PLAIN, { -1, 10, -1 } },
+};
+
+static CompoundVertex metalFrameTowerVtx2[] = {
+  { VTYPE_ANIMLIN, { 6, 10, static_cast<uint16>(-1), static_cast<uint16>(-1), 0 } },
+  { VTYPE_ANIMLIN, { 7, 11, static_cast<uint16>(-1), static_cast<uint16>(-1), 0 } },
+  { VTYPE_ANIMLIN, { 8, 12, static_cast<uint16>(-1), static_cast<uint16>(-1), 0 } },
+  { VTYPE_ANIMLIN, { 9, 13, static_cast<uint16>(-1), static_cast<uint16>(-1), 0 } },
+};
+
+static uint16 metalFrameTowerData[] = {
+  PTYPE_CYLINDER, 0x8000, 4, 6, 14, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 7, 15, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 8, 16, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 9, 17, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 6, 15, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 7, 14, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 7, 16, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 8, 15, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 8, 17, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 9, 16, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 6, 17, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 9, 14, 0, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 14, 15, 1, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 15, 16, 1, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 16, 17, 1, 10,
+  PTYPE_CYLINDER, 0x8000, 4, 17, 14, 1, 10,
+  PTYPE_END,
+};
+
+Model metalFrameTowerModel = { 0.1f, 20.0f, 14, metalFrameTowerVtx1, 14, 4, metalFrameTowerVtx2, 0,
+{ { 0, metalFrameTowerData, 0, 0, 0 } } };
+
+static PlainVertex starport1vtx1[] = {
+ 	{ VTYPE_PLAIN, { 0, 0, 0 } },
+ 	{ VTYPE_PLAIN, { 0, .01, 0 } },
+  { VTYPE_PLAIN, { -0.1, .01, -0.1 } },
+};
+
+static uint16 starport1data[] = {
+  PTYPE_MATFIXED, 30, 30, 30, 0, 0, 0, 0, 0, 0, 0,
+  PTYPE_CYLINDER, 0x8000, 8, 6, 7, 0, 50,
+  PTYPE_MATFIXED, 100, 100, 100, 0, 0, 0, 0, 0, 0, 0,
+  PTYPE_ZBIAS, 6, 1, 0,
+  PTYPE_TEXT, 0, 10, 8, 1, 0, 0, 0, 20,
+	PTYPE_ZBIAS, 0x8000, 0, 0,
+	PTYPE_SUBOBJECT, 0x8000, 100, 0, 1, 2, 100,
+	PTYPE_SUBOBJECT, 0x8000, 100, 3, 1, 2, 100,
+  PTYPE_END,
+};
+
+Model starport1model = { 100.0f, 55.0f, 9, starport1vtx1, 9, 0, dummyvtx2, 1,
+  { { 0, starport1data, 0, 0, 0 } } };
+
diff --git a/src/sbre/sbre_int.h b/src/sbre/sbre_int.h
index d40ed3a..f4731df 100644
--- a/src/sbre/sbre_int.h
+++ b/src/sbre/sbre_int.h
@@ -172,8 +172,10 @@ enum comptype {
 
 const int pCompSize[] = { 1, 3, 5, 3, 3, 2 };
 
-const char pModelString[1][256] = {
-  "IZRILGOOD",
+const char pModelString[][256] = {
+  "IZRILGOOD", "Rawr", "", "", "", "", "", "", "", "",
+  /* 10 - Landing pad messages. */
+  "1"
 };
 
 void RenderThrusters(RState* pState, int numThrusters, Thruster* pThrusters);
diff --git a/src/sbre/sbre_models.h b/src/sbre/sbre_models.h
index dcf471b..14c5ebd 100644
--- a/src/sbre/sbre_models.h
+++ b/src/sbre/sbre_models.h
@@ -4,7 +4,8 @@
 extern Model dishmodel, nosewheelmodel, nwunitmodel, mainwheelmodel, mwunitmodel;
 extern Model wing1model, wing2model;
 extern Model ship1model, ship2model, ship3model, ship4model, ship5model;
-extern Model station1model;
+extern Model station1model, starport1model;
+extern Model metalFrameTowerModel;
 
 /* Common subobject indices. */
 const int SUB_NOSEWHEEL = 1;
@@ -19,7 +20,7 @@ const int SUB_WING2     = 31;
 
 Model* const ppModel[] = {
   /* 0, current test object. */
-  &ship5model,
+  &ship2model,
   /* 1, common subobjects. */
   &nosewheelmodel,
   &nwunitmodel,
@@ -52,5 +53,8 @@ Model* const ppModel[] = {
   /* 80. */
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   /* 90, other people's ships. */
+  &starport1model, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	/* 100, more sub-objects. */
+	&metalFrameTowerModel
 };
 
diff --git a/src/sbre_viewer.cpp b/src/sbre_viewer.cpp
new file mode 100644
index 0000000..f096667
--- /dev/null
+++ b/src/sbre_viewer.cpp
@@ -0,0 +1,250 @@
+#include "libs.h"
+#include "sbre/sbre.h"
+#include "glfreetype.h"
+#include "gui.h"
+
+static SDL_Surface* g_screen;
+static int g_width, g_height;
+static int g_mouseMotion[2];
+static char g_keyState[SDLK_LAST];
+static int g_mouseButton[5];
+static int g_model = 0; /* sbre model number. Set with argc. */
+
+
+static void PollEvents(void) {
+  SDL_Event event;
+
+  g_mouseMotion[0] = g_mouseMotion[1] = 0;
+  while(SDL_PollEvent(&event)) {
+    Gui::HandleSDLEvent(&event);
+    switch(event.type) {
+      case SDL_KEYDOWN:
+        if(event.key.keysym.sym == SDLK_ESCAPE) { SDL_Quit(); exit(0); }
+        if(event.key.keysym.sym == SDLK_F11)      SDL_WM_ToggleFullScreen(g_screen);
+        g_keyState[event.key.keysym.sym] = 1;
+        break;
+      case SDL_KEYUP:
+        g_keyState[event.key.keysym.sym] = 0;
+        break;
+      case SDL_MOUSEBUTTONDOWN:
+        g_mouseButton[event.button.button] = 1;
+        /*L3D::onMouseButtonDown.emit(event.button.button,
+            event.button.x, event.button.y);*/
+        break;
+      case SDL_MOUSEBUTTONUP:
+        g_mouseButton[event.button.button] = 0;
+        /*L3D::onMouseButtonUp.emit(event.button.button,
+            event.button.x, event.button.y);*/
+        break;
+      case SDL_MOUSEMOTION:
+        g_mouseMotion[0] += event.motion.xrel;
+        g_mouseMotion[1] += event.motion.yrel;
+        break;
+      case SDL_QUIT:
+        SDL_Quit();
+        exit(0);
+        break;
+    }
+  }
+}
+
+static int    g_wheelMoveDir    = -1;
+static float  g_wheelPos        = 0;
+static bool   g_renderCollMesh  = false;
+static float  lightCol[4]       = { 1, 1, 1, 0 };
+static float  lightDir[4]       = { 0, 1, 0, 0 };
+static float  g_frameTime;
+
+static ObjParams params = {
+  { 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+  { 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f, 0.0f },
+
+  /* pColor[3] */
+  {
+    { { 1.0f, 0.0f, 1.0f }, { 0, 0, 0 }, { 0, 0, 0 }, 0 },
+    { { 0.8f, 0.6f, 0.5f }, { 0, 0, 0 }, { 0, 0, 0 }, 0 },
+    { { 0.5f, 0.5f, 0.5f }, { 0, 0, 0 }, { 0, 0, 0 }, 0 }
+  },
+
+  /* pText. */
+  { "IR-L33T", "ME TOO!" },
+};
+
+static void SetSbreParams(void) {
+  float gameTime = SDL_GetTicks() * 0.001;
+  params.pAnim[ASRC_SECFRAC]  = gameTime;
+  params.pAnim[ASRC_MINFRAC]  = gameTime / 60;
+  params.pAnim[ASRC_HOURFRAC] = gameTime / 3600.0f;
+  params.pAnim[ASRC_DAYFRAC]  = gameTime/ (24*3600.0f);
+  if(g_wheelPos <= 0) {
+    params.pAnim[ASRC_GEAR]   = 0;
+    params.pFlag[AFLAG_GEAR]  = 0;
+  } else {
+    params.pAnim[ASRC_GEAR]   = g_wheelPos;
+    params.pFlag[AFLAG_GEAR]  = 1;
+  }
+
+  /* Two seconds to move wheels. */
+  g_wheelPos += 0.5*g_frameTime*g_wheelMoveDir;
+  if(g_wheelPos < 0) g_wheelPos = 0;
+  if(g_wheelPos > 1) g_wheelPos = 1;
+}
+
+class Viewer: public Gui::Fixed {
+public:
+  Viewer() : Gui::Fixed(g_width, g_height) {
+    Gui::Screen::AddBaseWidget(this, 0, 0);
+    SetTransparency(true);
+    {
+      Gui::ToggleButton* b = new Gui::ToggleButton();
+      b->SetShortcut(SDLK_c, KMOD_NONE);
+      b->onChange.connect(sigc::mem_fun(*this, &Viewer::OnToggleCollMesh));
+      Add(b, 10, 10);
+      Add(new Gui::Label("[c] Show collision mesh."), 30, 10);
+    }
+    {
+      Gui::Button* b = new Gui::SolidButton();
+      b->SetShortcut(SDLK_g, KMOD_NONE);
+      b->onClick.connect(sigc::mem_fun(*this, &Viewer::OnToggleGearState));
+      Add(b, 10, 30);
+      Add(new Gui::Label("[g] Toggle gear state."), 30, 30);
+    }
+
+    ShowAll();
+    Show();
+  }
+
+  void OnToggleGearState(void) {
+    if(g_wheelMoveDir == -1) g_wheelMoveDir = +1;
+    else g_wheelMoveDir = -1;
+  }
+
+  void OnToggleCollMesh(Gui::ToggleButton* b, bool state) {
+    g_renderCollMesh = state;
+  }
+
+  void MainLoop();
+};
+
+void Viewer::MainLoop(void) {
+  matrix4x4d rot = matrix4x4d::Identity();
+  float distance = 100;
+  Uint32 lastFoo = SDL_GetTicks();
+
+  CollMesh* cmesh = (CollMesh*)calloc(1, sizeof(CollMesh));
+  sbreGenCollMesh(cmesh, g_model, &params, 1.0f);
+
+  for(;;) {
+    PollEvents();
+
+    if(g_keyState[SDLK_UP]) rot     = matrix4x4d::RotateXMatrix(g_frameTime)  * rot;
+    if(g_keyState[SDLK_DOWN]) rot   = matrix4x4d::RotateXMatrix(-g_frameTime) * rot;
+    if(g_keyState[SDLK_LEFT]) rot   = matrix4x4d::RotateYMatrix(g_frameTime)  * rot;
+    if(g_keyState[SDLK_RIGHT]) rot  = matrix4x4d::RotateYMatrix(-g_frameTime) * rot;
+    if(g_keyState[SDLK_EQUALS]) distance *= pow(0.5, g_frameTime);
+    if(g_keyState[SDLK_MINUS])  distance *= pow(2.0, g_frameTime);
+    if(g_mouseButton[1] || g_mouseButton[3]) {
+      float rx = 0.01*g_mouseMotion[1];
+      float ry = 0.01*g_mouseMotion[0];
+      rot = matrix4x4d::RotateXMatrix(rx) * rot;
+      rot = matrix4x4d::RotateYMatrix(ry) * rot;
+    }
+
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    float fracH = g_height / (float)g_width;
+    glFrustum(-1, 1, -fracH, fracH, 1.0f, 10000.0f);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    glClearColor(0,0,0,0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glPushAttrib(GL_ALL_ATTRIB_BITS);
+    SetSbreParams();
+    sbreSetViewport(g_width, g_height, g_width*0.5, 1.0f, 10000.0f, 0.0f, 1.0f);
+    sbreSetDirLight(lightCol, lightDir);
+
+    Matrix m;
+    Vector p;
+    m.x1 = rot[0]; m.x2 = rot[4]; m.x3 = rot[ 8];
+    m.y1 = rot[1]; m.y2 = rot[5]; m.y3 = rot[ 9];
+    m.z1 = rot[2]; m.z2 = rot[6]; m.z3 = rot[10];
+    p.x = 0; p.y = 0; p.z = distance;
+    if(g_renderCollMesh) sbreRenderCollMesh(cmesh, &p, &m);
+    else sbreRenderModel(&p, &m, g_model, &params);
+    glPopAttrib();
+
+    Gui::Draw();
+
+    SDL_GL_SwapBuffers();
+    g_frameTime = (SDL_GetTicks() - lastFoo) * 0.001;
+    lastFoo = SDL_GetTicks();
+  }
+}
+
+int main(int argc, char** argv) {
+  if((argc>1) && (0==strcmp(argv[1], "--help"))) {
+    printf("Usage:\n\nModelViewer <model number> <width> <height>\n");
+    exit(0);
+  }
+  if(argc > 1) {
+    g_model = atoi(argv[1]);
+  }
+  if(argc == 4) {
+    g_width = atoi(argv[2]);
+    g_height = atoi(argv[3]);
+  } else {
+    g_width = 800;
+    g_height = 600;
+  }
+
+  const SDL_VideoInfo* info = NULL;
+  if(SDL_Init(SDL_INIT_VIDEO) < 0) {
+    fprintf(stderr, "Video initialization failed: %s\n", SDL_GetError());
+    exit(-1);
+  }
+
+  info = SDL_GetVideoInfo();
+
+  SDL_GL_SetAttribute(SDL_GL_RED_SIZE,       8);
+  SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,     8);
+  SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,      8);
+  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,    24);
+  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,   1);
+  sbreSetZBias(2.0/(1<<24));
+  
+  Uint32 flags = SDL_OPENGL;
+
+  if((g_screen = SDL_SetVideoMode(g_width, g_height, info->vfmt->BitsPerPixel, flags)) == 0) {
+    /* Fall back to 16-bit depth buffer. */
+    SDL_GL_SetAttribute(SDL_GL_RED_SIZE,       5);
+    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,     6);
+    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,      5);
+    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,    16);
+    sbreSetZBias(2.0/(1<<16));
+    fprintf(stderr, "Failed to set video mode. (%s). Re-trying with 16-bit depth buffer.\n", SDL_GetError());
+    if((g_screen = SDL_SetVideoMode(g_width, g_height, info->vfmt->BitsPerPixel, flags)) == 0) {
+      fprintf(stderr, "Video mode set failed: %s\n", SDL_GetError());
+    }
+  }
+
+  glShadeModel(GL_SMOOTH);
+  glCullFace(GL_BACK);
+  glFrontFace(GL_CCW);
+  glEnable(GL_CULL_FACE);
+  glEnable(GL_DEPTH_TEST);
+  glEnable(GL_LIGHTING);
+  glEnable(GL_LIGHT0);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  glClearColor(0,0,0,0);
+  glViewport(0,0, g_width, g_height);
+  GLFTInit();
+  Gui::Init(g_width, g_height, g_width, g_height);
+
+  Viewer v;
+  v.MainLoop();
+}
+
+