Started implementing Version 4 of the model converter.
diff --git a/tools/ModelConverter/src/main.d b/tools/ModelConverter/src/main.d
index df2bbb1..758096d 100644
--- a/tools/ModelConverter/src/main.d
+++ b/tools/ModelConverter/src/main.d
@@ -97,6 +97,36 @@ mat4 Convert(ref const(aiMatrix4x4) pData){
   return result;
 }

+class BoneNode
+{
+  // For generating unique IDs
+  static ushort counter = 1;
+
+public:
+
+  // Hierarchy stuff
+  BoneNode* parent;
+  Vector!(BoneNode*) children;
+
+  // Actual bone data
+  ushort id;
+  const(char)[] name;
+  mat4 offsetMatrix;
+
+  this(const(aiBone)* bone)
+  {
+    id = counter++;
+    parent = null;
+    children = New!(Vector!(BoneNode*))();
+    name = bone.mName.data[0..bone.mName.length];
+    offsetMatrix = Convert(bone.mOffsetMatrix);
+  }
+
+  ~this()
+  {
+    Delete(children);
+  }
+}

 void ProgressModel(string path)
 {
@@ -194,6 +224,72 @@ void ProgressModel(string path)
       }
     }

+    auto uniqueBones = composite!( Hashmap!(const(char)[], ref BoneNode, StringHashPolicy) )(defaultCtor);
+    scope(exit){
+      foreach(bone; uniqueBones.keys)
+      {
+        Delete(bone);
+      }
+    }
+    // Collect unique bones
+    {
+      // Collect all unique bones from all meshes
+      for(size_t i=0; i < scene.mNumMeshes; i++)
+      {
+        const(aiMesh*) aimesh = scene.mMeshes[i];
+        for(size_t j = 0; j < aimesh.mNumBones; j++)
+        {
+          auto bone = aimesh.mBones[j];
+          auto key = bone.mName.data[0..bone.mName.length];
+          if(!uniqueBones.exists(key))
+          {
+            // TODO: Create BoneNode POINTERS and add them to the hashmap
+            auto tmp = New!(BoneNode)(bone);
+            uniqueBones[key] = tmp;
+          }
+        }
+      }
+    }
+
+    // Build bone hierarchy
+    {
+      // Define helper functions
+      void findBoneRoots(const(aiNode)* sceneNode, out Vector!(const(aiNode)*) boneRoots)
+      {
+        auto nodeName = sceneNode.mName.data[0..sceneNode.mName.length];
+        if(uniqueBones.exists(nodeName))
+        {
+          boneRoots.push_back(sceneNode);
+          return;
+        }
+        foreach(childNode; sceneNode.mChildren[0..sceneNode.mNumChildren])
+        {
+          findBoneRoots(childNode, boneRoots);
+        }
+      }
+      void buildHierarchy(const(aiNode)* sceneNode, BoneNode* parent)
+      {
+        foreach(childNode; sceneNode.mChildren[0..sceneNode.mNumChildren])
+        {
+          auto boneName = childNode.mName.data[0..childNode.mName.length];
+          auto bone = uniqueBones[boneName];
+          parent.children.push_back(bone);
+          buildHierarchy(childNode, bone);
+        }
+      }
+
+      // Begin the actual processing
+      auto boneRoots = composite!(Vector!(const(aiNode)*))(defaultCtor);
+      findBoneRoots(scene.mRootNode, boneRoots);
+
+      foreach(boneRoot ; boneRoots)
+      {
+        auto boneName = boneRoot.mName.data[0..boneRoot.mName.length];
+        buildHierarchy(boneRoot, uniqueBones[boneName]);
+      }
+
+    }
+
     //Size information
     {
       outFile.startWriteChunk("sizeinfo");
@@ -311,7 +407,6 @@ void ProgressModel(string path)
       }
     }

-
     //Materials
     {
       outFile.startWriteChunk("materials");
@@ -486,7 +581,7 @@ void ProgressModel(string path)

         struct BoneInfo
         {
-          ushort[4] boneIndices = [0, 0, 0, 0];
+          ushort[4] boneIds = [0, 0, 0, 0];
           float[4] boneWeights = [0, 0, 0, 0];
         }

@@ -529,7 +624,7 @@ void ProgressModel(string path)
               numBones[weight.mVertexId]++;

               bones[weight.mVertexId].boneWeights[index] = weight.mWeight;
-              bones[weight.mVertexId].boneIndices[index] = j;
+              bones[weight.mVertexId].boneIds[index] = uniqueBones[bone.mName.data[0..bone.mName.length]].id;
             }
           }
           outFile.writeArray(bones);
diff --git a/tools/ModelConverter/src/modeltypes.d b/tools/ModelConverter/src/modeltypes.d
index 45cd2e6..f686f5f 100644
--- a/tools/ModelConverter/src/modeltypes.d
+++ b/tools/ModelConverter/src/modeltypes.d
@@ -21,9 +21,10 @@ enum TextureType : ubyte {

 enum ModelFormatVersion : uint
 {
-  Version1 = 1, //Initial version
-  Version2 = 2, //saving material names
-  Version3 = 3  // Bones, baby!
+  Version1 = 1, // Initial version
+  Version2 = 2, // saving material names
+  Version3 = 3, // Bones, baby!
+  Version4 = 4  // Separate bone hierarchy.
 }

 enum PerVertexData : uint
Updated .gitignore file to ignore log and output of ModelConverter.
ModelConverter is now exporting the bones hierarchy.
diff --git a/tools/ModelConverter/src/ModelConverter.visualdproj b/tools/ModelConverter/src/ModelConverter.visualdproj
index c9bcd87..361eb0f 100644
--- a/tools/ModelConverter/src/ModelConverter.visualdproj
+++ b/tools/ModelConverter/src/ModelConverter.visualdproj
@@ -45,8 +45,8 @@
   <Dversion>2</Dversion>
   <ignoreUnsupportedPragmas>0</ignoreUnsupportedPragmas>
   <compiler>0</compiler>
-  <otherDMD>0</otherDMD>
-  <program>$(DMDInstallDir)windows\bin\dmd.exe</program>
+  <otherDMD>1</otherDMD>
+  <program>$(DMDInstallDir)windows\bin-nostd\dmd.exe</program>
   <imppath>$(ProjectDir)\..\..\..\..\druntime\import;$(ProjectDir)\..\..\..\..\phobos;$(ProjectDir)\..\..\..\..\thBase\src</imppath>
   <fileImppath />
   <outdir>..\$(ConfigurationName)</outdir>
diff --git a/tools/ModelConverter/src/main.d b/tools/ModelConverter/src/main.d
index 758096d..bb909fa 100644
--- a/tools/ModelConverter/src/main.d
+++ b/tools/ModelConverter/src/main.d
@@ -97,7 +97,7 @@ mat4 Convert(ref const(aiMatrix4x4) pData){
   return result;
 }

-class BoneNode
+struct BoneNode
 {
   // For generating unique IDs
   static ushort counter = 1;
@@ -224,9 +224,9 @@ void ProgressModel(string path)
       }
     }

-    auto uniqueBones = composite!( Hashmap!(const(char)[], ref BoneNode, StringHashPolicy) )(defaultCtor);
+    auto uniqueBones = composite!( Hashmap!(const(char)[], BoneNode*, StringHashPolicy) )(defaultCtor);
     scope(exit){
-      foreach(bone; uniqueBones.keys)
+      foreach(bone; uniqueBones.values)
       {
         Delete(bone);
       }
@@ -243,9 +243,7 @@ void ProgressModel(string path)
           auto key = bone.mName.data[0..bone.mName.length];
           if(!uniqueBones.exists(key))
           {
-            // TODO: Create BoneNode POINTERS and add them to the hashmap
-            auto tmp = New!(BoneNode)(bone);
-            uniqueBones[key] = tmp;
+            uniqueBones[key] = New!(BoneNode)(bone);
           }
         }
       }
@@ -254,7 +252,7 @@ void ProgressModel(string path)
     // Build bone hierarchy
     {
       // Define helper functions
-      void findBoneRoots(const(aiNode)* sceneNode, out Vector!(const(aiNode)*) boneRoots)
+      void findBoneRoots(const(aiNode)* sceneNode, Vector!(const(aiNode)*) boneRoots)
       {
         auto nodeName = sceneNode.mName.data[0..sceneNode.mName.length];
         if(uniqueBones.exists(nodeName))
@@ -273,6 +271,7 @@ void ProgressModel(string path)
         {
           auto boneName = childNode.mName.data[0..childNode.mName.length];
           auto bone = uniqueBones[boneName];
+          bone.parent = parent;
           parent.children.push_back(bone);
           buildHierarchy(childNode, bone);
         }
@@ -309,23 +308,30 @@ void ProgressModel(string path)
       }
       outFile.write(materialNameMemory);

-      // calc bone name memory
-      uint boneNameMemory = 0;
-      uint numBones = 0;
-      uint numBoneInfos = 0;
-      for(size_t i=0; i<scene.mNumMeshes; i++)
+      // calc boneNode memory
       {
-        const(aiMesh*) aimesh = scene.mMeshes[i];
-        for(size_t j = 0; j < aimesh.mNumBones; j++)
+        uint boneNameMemory = 0;
+        uint numChildren = 0;
+        foreach(bone; uniqueBones)
         {
-          boneNameMemory += aimesh.mBones[j].mName.length;
+          boneNameMemory += bone.name.length;
+          numChildren += bone.children.length;
         }
-        numBones += aimesh.mNumBones;
-        numBoneInfos += aimesh.mNumVertices;
+        outFile.write(boneNameMemory);
+        outFile.write(int_cast!uint(uniqueBones.count));
+        outFile.write(numChildren);
+      }
+
+      // calc bone memory of meshes
+      {
+        uint numBoneInfos = 0;
+        for(size_t i=0; i<scene.mNumMeshes; i++)
+        {
+          const(aiMesh*) aimesh = scene.mMeshes[i];
+          numBoneInfos += aimesh.mNumVertices;
+        }
+        outFile.write(numBoneInfos);
       }
-      outFile.write(boneNameMemory);
-      outFile.write(numBones);
-      outFile.write(numBoneInfos);

       outFile.write(scene.mNumMaterials);
       outFile.write(scene.mNumMeshes);
@@ -396,7 +402,7 @@ void ProgressModel(string path)
       outFile.startWriteChunk("textures");
       scope(exit){
         size_t size = outFile.endWriteChunk();
-        writefln("textures %d kb", size/1024);
+        writefln("textures %d kb (%d bytes)", size/1024, size);
       }

       //Write the collected results to the chunkfile
@@ -454,6 +460,34 @@ void ProgressModel(string path)
       }
     }

+    // Bone hierarchy
+    {
+      outFile.startWriteChunk("bones");
+      scope(exit){
+        size_t size = outFile.endWriteChunk();
+        writefln("bones (hierarchy) %d kb (%d bytes)", size/1024, size);
+      }
+
+      outFile.write(uniqueBones.count);
+      for(size_t i = 0; i < uniqueBones.count; ++i)
+      {
+        foreach(bone; uniqueBones)
+        {
+          if(bone.id == i + 1) // id of 0 is an invalid id
+          {
+            outFile.writeArray(bone.name);
+            outFile.write(bone.offsetMatrix);
+            outFile.write(int_cast!ushort(bone.parent is null ? 0 : bone.parent.id));
+            outFile.write(int_cast!uint(bone.children.length));
+            foreach(child; bone.children)
+            {
+              outFile.write(child.id);
+            }
+          }
+        }
+      }
+    }
+
     //Meshes
     {
       outFile.startWriteChunk("meshes");
@@ -593,21 +627,14 @@ void ProgressModel(string path)
           scope(exit)
           {
             size_t size = outFile.endWriteChunk();
-            writefln("bones %d kb", size / 1024);
-          }
-
-          outFile.write(int_cast!uint(aimesh.mNumBones));
-          foreach(bone; aimesh.mBones[0..aimesh.mNumBones])
-          {
-            outFile.writeArray(bone.mName.data[0..bone.mName.length]);
-            outFile.write(Convert(bone.mOffsetMatrix));
+            writefln("boneInfos %d kb", size / 1024);
           }

           //Inversing bone-vertex relations
-          // bones -> vertices ==> vertices -> bones
+          // boneInfos -> vertices ==> vertices -> boneInfos

-          auto bones = NewArray!BoneInfo(aimesh.mNumVertices);
-          scope(exit) Delete(bones);
+          auto boneInfos = NewArray!BoneInfo(aimesh.mNumVertices);
+          scope(exit) Delete(boneInfos);

           auto numBones = NewArray!ubyte(aimesh.mNumVertices);
           scope(exit) Delete(numBones);
@@ -615,19 +642,21 @@ void ProgressModel(string path)
           {
             foreach(weight; bone.mWeights[0..bone.mNumWeights])
             {
-              auto index = numBones[weight.mVertexId];
-              if(index >= 4)
+              auto numBonesOnVertex = numBones[weight.mVertexId];
+              if(numBonesOnVertex > 4)
               {
                 Error("Vertices that are influenced by more than 4 bones are NOT supported!");
               }

-              numBones[weight.mVertexId]++;
+              numBonesOnVertex++;
+              numBones[weight.mVertexId] = numBonesOnVertex;
+              auto boneIndexOnVertex = numBonesOnVertex - 1;

-              bones[weight.mVertexId].boneWeights[index] = weight.mWeight;
-              bones[weight.mVertexId].boneIds[index] = uniqueBones[bone.mName.data[0..bone.mName.length]].id;
+              boneInfos[weight.mVertexId].boneWeights[boneIndexOnVertex] = weight.mWeight;
+              boneInfos[weight.mVertexId].boneIds[boneIndexOnVertex] = uniqueBones[bone.mName.data[0..bone.mName.length]].id;
             }
           }
-          outFile.writeArray(bones);
+          outFile.writeArray(boneInfos);
         }

         //Faces
diff --git a/tools/ModelConverter/src/modeltypes.d b/tools/ModelConverter/src/modeltypes.d
index f686f5f..fe8846a 100644
--- a/tools/ModelConverter/src/modeltypes.d
+++ b/tools/ModelConverter/src/modeltypes.d
@@ -24,7 +24,6 @@ enum ModelFormatVersion : uint
   Version1 = 1, // Initial version
   Version2 = 2, // saving material names
   Version3 = 3, // Bones, baby!
-  Version4 = 4  // Separate bone hierarchy.
 }

 enum PerVertexData : uint
Merge branch 'merge64' of https://github.com/Manuzor/Spacecraft into merge64