diff --git a/CHANGES b/CHANGES
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,7 @@
+2011-10-29 - 0.2.3
+* fixed mkVMesh vertex buffer handling
+* added VMesh Binary instance and load support
+
 2011-07-02 - 0.2.2
 * fixed the handling of vertex colour attributes
 
diff --git a/Graphics/LambdaCube/Loader/VMesh.hs b/Graphics/LambdaCube/Loader/VMesh.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/LambdaCube/Loader/VMesh.hs
@@ -0,0 +1,127 @@
+module Graphics.LambdaCube.Loader.VMesh where
+
+import Control.Applicative
+import Data.Binary
+import Data.Vect
+import Foreign
+import qualified Data.ByteString.Lazy as LB
+import qualified Data.Vector as V
+import qualified Data.Vector.Mutable as M
+
+import Graphics.LambdaCube.RenderOperation
+import Graphics.LambdaCube.VertexBufferVector
+
+instance Binary Vec2 where
+    put (Vec2 a b) = put a >> put b
+    get = Vec2 <$> get <*> get
+
+instance Binary Vec3 where
+    put (Vec3 a b c) = put a >> put b >> put c
+    get = Vec3 <$> get <*> get <*> get
+
+instance Binary Vec4 where
+    put (Vec4 a b c d) = put a >> put b >> put c >> put d
+    get = Vec4 <$> get <*> get <*> get <*> get
+
+instance Binary Mat2 where
+    put (Mat2 a b) = put a >> put b
+    get = Mat2 <$> get <*> get
+
+instance Binary Mat3 where
+    put (Mat3 a b c) = put a >> put b >> put c
+    get = Mat3 <$> get <*> get <*> get
+
+instance Binary Mat4 where
+    put (Mat4 a b c d) = put a >> put b >> put c >> put d
+    get = Mat4 <$> get <*> get <*> get <*> get
+
+instance (Binary a) => Binary (V.Vector a) where
+    put v = do
+        put (V.length v)
+        mapM_ put (V.toList v)
+
+    -- this is morally sound, if very awkward.
+    -- all effects are contained, and can't escape the unsafeFreeze
+    {-# INLINE get #-}
+    get = do
+        n  <- get
+
+        -- new unitinialized array
+        mv <- lift $ M.new n
+
+        let fill i
+                | i < n = do
+                    x <- get
+                    (unsafePerformIO $ M.unsafeWrite mv i x) `seq` return ()
+                    fill (i+1)
+
+                | otherwise = return ()
+
+        fill 0
+
+        lift $ V.unsafeFreeze mv
+
+lift = return .unsafePerformIO
+
+instance Binary VectorVertexData where
+    put (VVD_POSITION a)             = putWord8 0 >> put a
+    put (VVD_BLEND_WEIGHTS a)        = putWord8 1 >> put a
+    put (VVD_BLEND_INDICES a)        = putWord8 2 >> put a
+    put (VVD_NORMAL a)               = putWord8 3 >> put a
+    put (VVD_DIFFUSE a)              = putWord8 4 >> put a
+    put (VVD_SPECULAR a)             = putWord8 5 >> put a
+    put (VVD_TEXTURE_COORDINATES1 a) = putWord8 6 >> put a
+    put (VVD_TEXTURE_COORDINATES2 a) = putWord8 7 >> put a
+    put (VVD_TEXTURE_COORDINATES3 a) = putWord8 8 >> put a
+    put (VVD_BINORMAL a)             = putWord8 9 >> put a
+    put (VVD_TANGENT a)              = putWord8 10 >> put a
+    get = do
+        tag_ <- getWord8
+        case tag_ of
+            0 -> VVD_POSITION <$> get
+            1 -> VVD_BLEND_WEIGHTS <$> get
+            2 -> VVD_BLEND_INDICES <$> get
+            3 -> VVD_NORMAL <$> get
+            4 -> VVD_DIFFUSE <$> get
+            5 -> VVD_SPECULAR <$> get
+            6 -> VVD_TEXTURE_COORDINATES1 <$> get
+            7 -> VVD_TEXTURE_COORDINATES2 <$> get
+            8 -> VVD_TEXTURE_COORDINATES3 <$> get
+            9 -> VVD_BINORMAL <$> get
+            10 -> VVD_TANGENT <$> get
+            _ -> fail "no parse"
+
+instance Binary OperationType where
+    put a = put (fromEnum a)
+    get = toEnum <$> get
+
+putIndices :: Maybe (V.Vector Int) -> Put
+putIndices (Just i) = putWord8 1 >> put (V.map (fromIntegral :: Int -> Int32) i)
+putIndices Nothing  = putWord8 0
+
+getIndices :: Get (Maybe (V.Vector Int))
+getIndices = do
+    m <- getWord8
+    case m of
+        1 -> Just <$> V.map (fromIntegral :: Int32 -> Int) <$> get
+        _ -> return Nothing
+
+instance Binary VSubMesh where
+    put (VSubMesh a b c d) = put a >> put b >> put c >> putIndices d
+    get = VSubMesh <$> get <*> get <*> get <*> getIndices
+
+instance Binary VMesh where
+    put (VMesh a b) = put a >> put b
+    get = VMesh <$> get <*> get
+
+decodeVMesh :: LB.ByteString -> VMesh
+decodeVMesh d = decode d
+
+encodeVMesh :: VMesh -> LB.ByteString
+encodeVMesh vm = encode vm
+
+loadVMesh :: FilePath -> IO VMesh
+loadVMesh name = decodeFile name
+
+saveVMesh :: FilePath -> VMesh -> IO ()
+saveVMesh name vm = encodeFile name vm
diff --git a/Graphics/LambdaCube/MeshUtil.hs b/Graphics/LambdaCube/MeshUtil.hs
--- a/Graphics/LambdaCube/MeshUtil.hs
+++ b/Graphics/LambdaCube/MeshUtil.hs
@@ -34,14 +34,18 @@
 mkVMesh' :: [(Proj4,VMesh)] -> VMesh
 mkVMesh' vml = VMesh [joinGroup g | g <- groupByGeometry $ groupByMaterial $ vertexData vml] Nothing
 
+-- FIXME: problem shared geom vs private geom
 vertexData :: [(Proj4,VMesh)] -> [MeshGroup]
 vertexData l =
-    [ (materialName, sortedVData svData vData, iData, proj, opType)
-    | (proj, VMesh subMeshList (Just svData)) <- l
+    [ (materialName, sortedVData gData vData, iData, proj, opType)
+    | (proj, VMesh subMeshList gData) <- l
     , VSubMesh materialName opType vData iData <- subMeshList
     ]
   where
-    sortedVData svd = V.modify (V.sortBy (comparing vectorVertexType)) . fromMaybe svd
+    sortedVData local global = V.modify (V.sortBy (comparing vectorVertexType)) $ case (local,global) of
+        (Just a, _) -> a
+        (Nothing, Just a) -> a
+        _ -> error "illegal mesh format"
 
 groupByMaterial :: [MeshGroup] -> [[MeshGroup]]
 groupByMaterial = groupSetBy (\(a,_,_,_,_) (b,_,_,_,_) -> compare a b)
diff --git a/Graphics/LambdaCube/World.hs b/Graphics/LambdaCube/World.hs
--- a/Graphics/LambdaCube/World.hs
+++ b/Graphics/LambdaCube/World.hs
@@ -35,6 +35,7 @@
 import Graphics.LambdaCube.Image
 import Graphics.LambdaCube.Light
 import Graphics.LambdaCube.Loader.MeshXML
+import Graphics.LambdaCube.Loader.VMesh
 import Graphics.LambdaCube.Loader.ResourceScript
 import Graphics.LambdaCube.Material
 import Graphics.LambdaCube.Mesh
@@ -325,6 +326,9 @@
         ".xml" -> do
             putStrLn $ "ResourceLibrary " ++ "parsing mesh XML file " ++ name
             parseMesh wrRenderSystem $ SB8.unpack f
+        ".vmesh" -> do
+            putStrLn $ "ResourceLibrary " ++ "parsing VMesh file " ++ name
+            meshFromV wrRenderSystem $ decodeVMesh $ LB.fromChunks [f]
         _ -> error "Unsupported format!"
         {-
          f <- loadFile (rlResourceGroups rl) name
diff --git a/lambdacube-engine.cabal b/lambdacube-engine.cabal
--- a/lambdacube-engine.cabal
+++ b/lambdacube-engine.cabal
@@ -1,5 +1,5 @@
 name: lambdacube-engine
-version: 0.2.2
+version: 0.2.3
 cabal-version: >= 1.6 && < 2
 build-type: Simple
 license: BSD3
@@ -76,6 +76,7 @@
     Graphics.LambdaCube.Loader.Generated.ResourceScriptScanner
     Graphics.LambdaCube.Loader.MaterialScript
     Graphics.LambdaCube.Loader.MeshXML
+    Graphics.LambdaCube.Loader.VMesh
     Graphics.LambdaCube.Loader.ParserUtil
     Graphics.LambdaCube.Loader.ResourceScript
     Graphics.LambdaCube.Loader.SkeletonXML
