diff --git a/src/Makefile.am b/src/Makefile.am
index 797898d..cec8087 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,7 +8,7 @@ Lephisto3D_SOURCES	= main.cpp gui_button.cpp gui.cpp gui_fixed.cpp gui_screen.cp
 		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 static_rigid_body.cpp ship_type.cpp \
-                info_view.cpp
+    info_view.cpp model_coll_mesh_data.cpp
 Lephisto3D_LDADD = sbre/libsbre.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 \
@@ -16,5 +16,5 @@ include_HEADERS = body.h frame.h generic_system_view.h glfreetype.h gui_button.h
 		gui_radio_group.h gui_screen.h gui_toggle_button.h gui_widget.h libs.h matrix4x4.h mtrand.h objimport.h l3d.h \
 		planet.h player.h rigid_body.h sector.h sector_view.h ship_cpanel.h ship.h space.h star.h star_system.h system_info_view.h \
 		system_view.h vector3.h view.h world_view.h date.h space_station.h space_station_view.h static_rigid_body.h gui_iselectable.h \
-		ship_type.h object.h info_view.h
+		ship_type.h object.h info_view.h model_coll_mesh_data.h
 
diff --git a/src/model_coll_mesh_data.cpp b/src/model_coll_mesh_data.cpp
new file mode 100644
index 0000000..9e91084
--- /dev/null
+++ b/src/model_coll_mesh_data.cpp
@@ -0,0 +1,102 @@
+#include <map>
+#include <algorithm>
+#include "libs.h"
+#include "model_coll_mesh_data.h"
+#include "sbre/sbre.h"
+
+/*
+ * In order to do space station doors and other flagged bits of collision
+ * meshes, we have to make a different dTriMeshData for each.
+ * Vertex indices for each flagged bit of the mesh need to be contiguous,
+ * so we waste some time and memory making this so.
+ */
+struct coltri_compare : public std::binary_function<coltri_t, coltri_t, bool> {
+  bool operator()(coltri_t a, coltri_t b) { return a.flags < b.flags; }
+};
+
+static ObjParams params = {
+  { 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+  { 0.f, 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[3][256]. */
+  { "", "" },
+};
+
+/* Indexed by sbre model ID. */
+static std::map<int, CollMeshSet*> modelCollStuff;
+
+void CollMeshSet::GetMeshParts(void) {
+  numMeshParts = 1;
+  int curFlag = triIndices[0].flags;
+  for(int i = 0; i < sbreCollMesh->ni/3; i++) {
+    if(curFlag != triIndices[i].flags) numMeshParts++;
+    curFlag = triIndices[i].flags;
+  }
+  printf("%d parts\n", numMeshParts);
+  meshParts = new dTriMeshDataID[numMeshParts];
+  meshFlags = new int[numMeshParts];
+
+  int tidx = 0;
+  int midx = 0;
+  do {
+    int len = 0;
+    int flag = triIndices[tidx].flags;
+    while((len < sbreCollMesh->ni/3) && (triIndices[tidx+len].flags == flag)) len++;
+    printf("%d: len %d\n", tidx, len);
+
+    dTriMeshDataID triMeshDataID = dGeomTriMeshDataCreate();
+    dGeomTriMeshDataBuildSingle(triMeshDataID, (void*)sbreCollMesh->pVertex,
+                                3*sizeof(float), sbreCollMesh->nv, (void*)&triIndices[tidx],
+                                3*len, 4*sizeof(int));
+    meshFlags[midx] = flag;
+    meshParts[midx++] = triMeshDataID;
+    tidx += len;
+  } while(tidx < sbreCollMesh->ni/3);
+}
+
+CollMeshSet::CollMeshSet(int sbreModel) {
+  sbreCollMesh = (CollMesh*)calloc(1, sizeof(CollMesh));
+  sbreGenCollMesh(sbreCollMesh, sbreModel, &params);
+  /* TODO: Flip Z & X because sbre is in magicspace. */
+  for(int i = 0; i < 3*sbreCollMesh->nv; i += 3) {
+    sbreCollMesh->pVertex[i] = -sbreCollMesh->pVertex[i];
+    sbreCollMesh->pVertex[i+2] = -sbreCollMesh->pVertex[i+2];
+  }
+
+  triIndices = new coltri_t[sbreCollMesh->ni/3];
+  int tidx = 0;
+  /* Copy the tri indices into our lovely coltri_t array. */
+  for(int i = 0; i < sbreCollMesh->ni; i += 3) {
+    triIndices[tidx].v1 = sbreCollMesh->pIndex[i];
+    triIndices[tidx].v2 = sbreCollMesh->pIndex[i+1];
+    triIndices[tidx].v3 = sbreCollMesh->pIndex[i+2];
+    triIndices[tidx].flags = sbreCollMesh->pFlag[i/3];
+    tidx++;
+  }
+  /* Sort collmesh tris by flag, ascending. */
+  sort(triIndices, triIndices+(sbreCollMesh->ni/3), coltri_compare());
+
+  GetMeshParts();
+}
+
+CollMeshSet* GetModelCollMeshSet(int sbreModel) {
+  std::map<int, CollMeshSet*>::iterator it = modelCollStuff.find(sbreModel);
+  if(it != modelCollStuff.end()) return (*it).second;
+
+  CollMeshSet* cstuff = new CollMeshSet(sbreModel);
+  modelCollStuff[sbreModel] = cstuff;
+  return cstuff;
+}
+
+CollMesh* GetModelSBRECollMesh(int sbreModel) {
+  return modelCollStuff[sbreModel]->sbreCollMesh;
+}
+
diff --git a/src/model_coll_mesh_data.h b/src/model_coll_mesh_data.h
new file mode 100644
index 0000000..889a909
--- /dev/null
+++ b/src/model_coll_mesh_data.h
@@ -0,0 +1,24 @@
+#pragma once
+#include "libs.h"
+#include "sbre/sbre.h"
+
+struct coltri_t {
+  int v1, v2, v3, flags;
+};
+
+class CollMeshSet {
+public:
+  CollMesh* sbreCollMesh;
+  coltri_t* triIndices;
+  dTriMeshDataID* meshParts;
+  int* meshFlags;
+  int numMeshParts;
+
+  CollMeshSet(int sbreModel);
+private:
+  void GetMeshParts(void);
+};
+
+CollMeshSet* GetModelCollMeshSet(int sbreModel);
+CollMesh* GetModelSBRECollMesh(int sbreModel);
+
diff --git a/src/object.h b/src/object.h
index b1874f3..925393e 100644
--- a/src/object.h
+++ b/src/object.h
@@ -2,7 +2,7 @@
 
 class Object {
 public:
-  enum Type { NONE, BODY, SHIP, SPACESTATION, LASER };
+  enum Type { NONE, BODY, SHIP, SPACESTATION, LASER, GEOM };
   virtual Type GetType(void) = 0;
 };
 
diff --git a/src/sbre/models.cpp b/src/sbre/models.cpp
index 3923db6..24a9d21 100644
--- a/src/sbre/models.cpp
+++ b/src/sbre/models.cpp
@@ -672,10 +672,10 @@ static PlainVertex station1vtx1[] = {
   { VTYPE_PLAIN, { 0.0f, 0.0f, 25.0f } },  /* 34, ring start, end. */
   { VTYPE_PLAIN, { 0.0f, 0.0f, -25.0f } },
 
-  { VTYPE_PLAIN, { -10.0f, 5.0f, 10.0f } },
-  { VTYPE_PLAIN, { 10.0f, 5.0f, 10.0f } },    /* 36, inlet middle (for docking). */
-  { VTYPE_PLAIN, { 10.0f, -5.0f, 10.0f } },
-  { VTYPE_PLAIN, { -10.0f, -5.0f, 10.0f } },
+  { VTYPE_PLAIN, { -9.0f, 4.5f, 10.0f } },
+  { VTYPE_PLAIN, { 9.0f, 4.5f, 10.0f } },    /* 36, inlet middle (for docking). */
+  { VTYPE_PLAIN, { 9.0f, -4.5f, 10.0f } },
+  { VTYPE_PLAIN, { -9.0f, -4.5f, 10.0f } },
 
 #if 0
     { VTYPE_PLAIN, { 0.0f, 120.0f, 15.0f } },   /* 34, ring top. */
diff --git a/src/ship.cpp b/src/ship.cpp
index 2169ed6..fd5a080 100644
--- a/src/ship.cpp
+++ b/src/ship.cpp
@@ -4,6 +4,7 @@
 #include "l3d.h"
 #include "world_view.h"
 #include "space.h"
+#include "model_coll_mesh_data.h"
 
 static ObjParams params = {
   { 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
@@ -38,8 +39,8 @@ Ship::Ship(ShipType::Type shipType) : RigidBody() {
   memset(m_thrusters, 0, sizeof(m_thrusters)); 
   const ShipType& stype = GetShipType();
   SetGeomFromSBREModel(stype.sbreModel, &params);
-  SetMassDistributionFromCollMesh(sbreCollMesh);
-  dGeomSetBody(m_geom, m_body);
+  SetMassDistributionFromCollMesh(GetModelSBRECollMesh(stype.sbreModel));
+  GeomsSetBody(m_body);
   UpdateMass();
 }
 
diff --git a/src/space.cpp b/src/space.cpp
index 06f9ba9..88d275e 100644
--- a/src/space.cpp
+++ b/src/space.cpp
@@ -132,7 +132,9 @@ static bool _OnCollision(dGeomID g1, dGeomID g2, Object* o1, Object* o2,
     }
     Ship::LaserObj* lobj = static_cast<Ship::LaserObj*>(o2);
     if(o1 == lobj->owner) return false;
-    printf("%s was shot by %s\n", ((Body*)o1)->GetLabel().c_str(), lobj->owner->GetLabel().c_str());
+    printf("%s (geom flag %x) was shot by %s\n", ((StaticRigidBody::Geom*)o1)->parent->GetLabel().c_str(),
+        ((StaticRigidBody::Geom*)o1)->flags, lobj->owner->GetLabel().c_str());
+
     if(o1->GetType() == Object::SHIP) {
       RigidBody* rb = (RigidBody*)o1;
       dVector3 start, dir;
@@ -147,8 +149,19 @@ static bool _OnCollision(dGeomID g1, dGeomID g2, Object* o1, Object* o2,
     }
     return false;
   } else {
-    Body* pb1 = static_cast<Body*>(o1);
-    Body* pb2 = static_cast<Body*>(o2);
+    Body* pb1, *pb2;
+    int flags = 0;
+    /* Geom bodies point to their parents. */
+    if(o1->GetType() == Object::GEOM) {
+      pb1 = static_cast<StaticRigidBody::Geom*>(o1)->parent;
+      flags |= static_cast<StaticRigidBody::Geom*>(o1)->flags;
+    } else pb1 = static_cast<Body*>(o1);
+    if(o2->GetType() == Object::GEOM) {
+      pb2 = static_cast<StaticRigidBody::Geom*>(o2)->parent;
+      flags |= static_cast<StaticRigidBody::Geom*>(o2)->flags;
+    } else pb2 = static_cast<Body*>(o2);
+
+    printf("Collision flags %x\n", flags);
     if((pb1 && !pb1->OnCollision(pb2)) || (pb2 && !pb2->OnCollision(pb1))) return false;
   }
   return true;
diff --git a/src/space_station.cpp b/src/space_station.cpp
index 15eb553..5279ced 100644
--- a/src/space_station.cpp
+++ b/src/space_station.cpp
@@ -25,8 +25,8 @@ SpaceStation::SpaceStation(void) : StaticRigidBody() {
   matrix4x4d m = matrix4x4d::RotateYMatrix(-M_PI/4);
   dMatrix3 _m;
   m.SaveToOdeMatrix(_m);
-  dGeomSetRotation(m_geom, _m);
-  dGeomSetBody(m_geom, 0);
+  //dGeomSetRotation(m_geom, _m);
+  //dGeomSetBody(m_geom, 0);
 }
 
 SpaceStation::~SpaceStation(void) {
diff --git a/src/static_rigid_body.cpp b/src/static_rigid_body.cpp
index 4a6e9a5..c8a1bea 100644
--- a/src/static_rigid_body.cpp
+++ b/src/static_rigid_body.cpp
@@ -5,51 +5,44 @@
 #include "frame.h"
 #include "l3d.h"
 #include "world_view.h"
+#include "model_coll_mesh_data.h"
 
 StaticRigidBody::StaticRigidBody(void): Body() {
-  m_geom = 0;
-  sbreCollMesh = 0;
   triMeshLastMatrixIndex = 0;
 }
 
 StaticRigidBody::~StaticRigidBody(void) {
-  if(sbreCollMesh) {
-    free(sbreCollMesh->pVertex);
-    free(sbreCollMesh->pIndex);
-    free(sbreCollMesh->pFlag);
-    free(sbreCollMesh);
-  }
   SetFrame(0); /* Will remove geom from frame if necessary. */
-  dGeomDestroy(m_geom);
+  for(unsigned int i = 0; i < geoms.size(); i++) {
+    dGeomDestroy(geoms[i]);
+  }
 }
 
-void StaticRigidBody::SetGeomSphere(double radius) {
-  assert(!m_geom);
-  m_geom = dCreateSphere(0, radius);
+void StaticRigidBody::GeomsSetBody(dBodyID body) {
+  for(unsigned int i = 0; i < geoms.size(); i++) {
+    dGeomSetBody(geoms[i], body);
+  }
 }
 
 void StaticRigidBody::SetGeomFromSBREModel(int sbreModel, ObjParams* params) {
-  assert(!m_geom);
-  assert(sbreCollMesh == 0);
-  sbreCollMesh = (CollMesh*)calloc(1, sizeof(CollMesh));
-  sbreGenCollMesh(sbreCollMesh, sbreModel, params);
-  /* TODO: Flip Z & X because SBRE is in magicspace. */
-  for(int i = 0; i < 3*sbreCollMesh->nv; i += 3) {
-    sbreCollMesh->pVertex[i]    = -sbreCollMesh->pVertex[i];
-    sbreCollMesh->pVertex[i+2]  = -sbreCollMesh->pVertex[i+2];
-  }
-  dTriMeshDataID triMeshDataID = dGeomTriMeshDataCreate();
-  dGeomTriMeshDataBuildSingle(triMeshDataID, (void*)sbreCollMesh->pVertex,
-          3*sizeof(float), sbreCollMesh->nv, (void*)sbreCollMesh->pIndex,
-          sbreCollMesh->ni, 3*sizeof(int));
+  assert(geoms.size() == 0);
+  CollMeshSet* mset = GetModelCollMeshSet(sbreModel);
 
-  /* TODO: Leaking StaticRigidBody m_geom. */
-  m_geom = dCreateTriMesh(0, triMeshDataID, NULL, NULL, NULL);
-  dGeomSetData(m_geom, static_cast<Body*>(this));
+  geomColl.resize(mset->numMeshParts);
+  geoms.resize(mset->numMeshParts);
+
+  for(unsigned int i = 0; i < mset->numMeshParts; i++) {
+    geoms[i] = dCreateTriMesh(0, mset->meshParts[i], NULL, NULL, NULL);
+    geomColl[i].parent = this;
+    geomColl[i].flags = mset->meshFlags[i];
+    dGeomSetData(geoms[i], static_cast<Object*>(&geomColl[i]));
+  }
 }
 
 void StaticRigidBody::SetPosition(vector3d p) {
-  dGeomSetPosition(m_geom, p.x, p.y, p.z);
+  for(unsigned int i = 0; i < geoms.size(); i++) {
+    dGeomSetPosition(geoms[i], p.x, p.y, p.z);
+  }
 }
 
 void StaticRigidBody::SetVelocity(vector3d v) {
@@ -57,12 +50,12 @@ void StaticRigidBody::SetVelocity(vector3d v) {
 }
 
 vector3d StaticRigidBody::GetPosition(void) {
-  const dReal* pos = dGeomGetPosition(m_geom);
+  const dReal* pos = dGeomGetPosition(geoms[0]);
   return vector3d(pos[0], pos[1], pos[2]);
 }
 
 void StaticRigidBody::GetRotMatrix(matrix4x4d& m) {
-  m.LoadFromOdeMatrix(dGeomGetRotation(m_geom));
+  m.LoadFromOdeMatrix(dGeomGetRotation(geoms[0]));
 }
 
 void StaticRigidBody::ViewingRotation(void) {
@@ -73,7 +66,7 @@ void StaticRigidBody::ViewingRotation(void) {
 }
 
 void StaticRigidBody::TransformCameraTo(void) {
-  const dReal* p = dGeomGetPosition(m_geom);
+  const dReal* p = dGeomGetPosition(geoms[0]);
   matrix4x4d m;
   GetRotMatrix(m);
   m = m.InverseOf();
@@ -84,7 +77,7 @@ void StaticRigidBody::TransformCameraTo(void) {
 void StaticRigidBody::TransformToModelCoords(const Frame* camFrame) {
   vector3d fpos = GetPositionRelTo(camFrame);
 
-  const dReal* r = dGeomGetRotation(m_geom);
+  const dReal* r = dGeomGetRotation(geoms[0]);
   matrix4x4d m;
   m[ 0] = r[ 0]; m[ 1] = r[ 4]; m[ 2] = r[ 8]; m[ 3] = 0;
   m[ 4] = r[ 1]; m[ 5] = r[ 5]; m[ 6] = r[ 9]; m[ 7] = 0;
@@ -94,14 +87,22 @@ void StaticRigidBody::TransformToModelCoords(const Frame* camFrame) {
 }
 
 void StaticRigidBody::SetFrame(Frame* f) {
-  if(GetFrame()) GetFrame()->RemoveGeom(m_geom);
+  if(GetFrame()) {
+    for(unsigned int i = 0; i < geoms.size(); i++) {
+      GetFrame()->RemoveGeom(geoms[i]);
+    }
+  }
   Body::SetFrame(f);
-  if(f) f->AddGeom(m_geom);
+  if(f) {
+    for(unsigned int i = 0; i < geoms.size(); i++) {
+      f->AddGeom(geoms[i]);
+    }
+  }
 }
 
 void StaticRigidBody::TriMeshUpdateLastPos(void) {
   /* ODE tri mesh likes to know our old position. */
-  const dReal* r = dGeomGetRotation(m_geom);
+  const dReal* r = dGeomGetRotation(geoms[0]);
   vector3d pos = GetPosition();
   dReal* t = triMeshTrans + 16*triMeshLastMatrixIndex;
   t[ 0] = r[0]; t[1] = r[1]; t[ 2] = r[ 2]; t[ 3] = 0;
@@ -109,7 +110,9 @@ void StaticRigidBody::TriMeshUpdateLastPos(void) {
   t[ 8] = r[8]; t[9] = r[9]; t[10] = r[10]; t[11] = 0;
   t[12] = pos.x; t[13] = pos.y; t[14] = pos.z; t[15] = 1;
   triMeshLastMatrixIndex = !triMeshLastMatrixIndex;
-  dGeomTriMeshSetLastTransform(m_geom, *(dMatrix4*)(triMeshTrans + 16*triMeshLastMatrixIndex));
+  for(unsigned int i = 0; i < geoms.size(); i++) {
+    dGeomTriMeshSetLastTransform(geoms[i], *(dMatrix4*)(triMeshTrans + 16*triMeshLastMatrixIndex));
+  }
 }
 
 void StaticRigidBody::RenderSbreModel(const Frame* camFrame, int model, ObjParams* params) {
@@ -145,7 +148,7 @@ void StaticRigidBody::RenderSbreModel(const Frame* camFrame, int model, ObjParam
   pos = L3D::world_view->viewingRotation*pos;
   Vector p; p.x = pos.x; p.y = pos.y; p.z = -pos.z;
   matrix4x4d rot;
-  rot.LoadFromOdeMatrix(dGeomGetRotation(m_geom));
+  rot.LoadFromOdeMatrix(dGeomGetRotation(geoms[0]));
   rot = L3D::world_view->viewingRotation * rot;
   Matrix m;
   m.x1 =  rot[0]; m.x2 =  rot[4]; m.x3 = -rot[8];
diff --git a/src/static_rigid_body.h b/src/static_rigid_body.h
index 3e16343..321160b 100644
--- a/src/static_rigid_body.h
+++ b/src/static_rigid_body.h
@@ -1,4 +1,5 @@
 #pragma once
+#include <vector>
 #include "body.h"
 #include "vector3.h"
 #include "matrix4x4.h"
@@ -21,16 +22,22 @@ public:
   void ViewingRotation(void);
   void GetRotMatrix(matrix4x4d& m);
   virtual void SetFrame(Frame* f);
+  void GeomsSetBody(dBodyID body);
 
   void TriMeshUpdateLastPos(void);
   void SetGeomFromSBREModel(int sbreModel, ObjParams* params);
-  void SetGeomSphere(double radius);
 
   void RenderSbreModel(const Frame* camFrame, int model, ObjParams* params);
-protected:
-  dGeomID m_geom;
-  CollMesh* sbreCollMesh;
-  float* meshNormals;
+  
+  class Geom : public Object {
+  public:
+    virtual Type GetType(void) { return Object::GEOM; }
+    Body* parent;
+    int flags;
+  };
+private:
+  std::vector<Geom> geomColl;
+  std::vector<dGeomID> geoms;
   dReal triMeshTrans[32];
   int triMeshLastMatrixIndex;
 };