Browse Source

New mesh format

Panagiotis Christopoulos Charitos 11 years ago
parent
commit
c91b3a1c4f

+ 172 - 0
include/anki/resource/MeshLoader2.h

@@ -0,0 +1,172 @@
+// Copyright (C) 2014, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#ifndef ANKI_RESOURCE_MESH_LOADER_2_H
+#define ANKI_RESOURCE_MESH_LOADER_2_H
+
+#include "anki/resource/Common.h"
+#include "anki/Math.h"
+
+namespace anki {
+
+/// Mesh data. This class loads the mesh file and the Mesh class loads it to
+/// the GPU.
+class MeshLoader2
+{
+public:
+	/// Type of the components.
+	enum class ComponentFormat: U32
+	{
+		NONE,
+
+		R8,
+		R8G8,
+		R8G8B8,
+		R8G8B8A8,
+
+		R16,
+		R16G16,
+		R16G16B16,
+		R16G16B16A16,
+
+		R32,
+		R32G32,
+		R32G32B32,
+		R32G32B32A32,
+
+		R10G10B10A2,
+
+		COUNT
+	};
+
+	enum class FormatTransform: U32
+	{
+		NONE,
+
+		UNORM,
+		SNORM,
+		UINT,
+		SINT,
+		FLOAT,
+
+		COUNT
+	};
+
+	struct Format
+	{
+		ComponentFormat m_components = ComponentFormat::NONE;
+		FormatTransform m_transform = FormatTransform::NONE;
+	};
+
+	struct Header
+	{
+		Array<U8, 8> m_magic; ///< Magic word.
+		U32 m_flags;
+		U32 m_flags2;
+		Format m_positionsFormat;
+		Format m_normalsFormat;
+		Format m_tangentsFormat;
+		Format m_colorsFormat; ///< Vertex color.
+		Format m_uvsFormat;
+		Format m_boneWeightsFormat;
+		Format m_boneIndicesFormat;
+		Format m_indicesFormat; ///< Vertex indices.
+
+		U32 m_totalIndicesCount;
+		U32 m_totalVerticesCount;
+		/// Number of UV sets. Eg one for normal diffuse and another for 
+		/// lightmaps.
+		U32 m_uvsChannelCount;
+		U32 m_subMeshCount;
+
+		U8 m_padding[32];
+	};
+
+	static_assert(sizeof(Header) == 128, "Check size of struct");
+
+	struct SubMesh
+	{
+		U32 m_firstIndex = 0;
+		U32 m_indicesCount = 0;
+	};
+
+	~MeshLoader2();
+
+	ANKI_USE_RESULT Error load(
+		TempResourceAllocator<U8> alloc,
+		const CString& filename);
+
+	const Header& getHeader() const
+	{
+		ANKI_ASSERT(isLoaded());
+		return m_header;
+	}
+
+	const U8* getVertexData() const
+	{
+		ANKI_ASSERT(isLoaded());
+		return &m_verts[0];
+	}
+
+	PtrSize getVertexDataSize() const
+	{
+		ANKI_ASSERT(isLoaded());
+		return m_verts.getSizeInBytes();
+	}
+
+	PtrSize getVertexSize() const
+	{
+		ANKI_ASSERT(isLoaded());
+		return m_vertSize;
+	}
+
+	const U8* getIndexData() const
+	{
+		ANKI_ASSERT(isLoaded());
+		return &m_indices[0];
+	}
+
+	PtrSize getIndexDataSize() const
+	{
+		ANKI_ASSERT(isLoaded());
+		return m_indices.getSizeInBytes();
+	}
+
+	Bool hasVertexWeights() const
+	{
+		ANKI_ASSERT(isLoaded());
+		return 
+			m_header.m_boneWeightsFormat.m_components != ComponentFormat::NONE;
+	}
+
+private:
+	template<typename T>
+	using MDArray = DArray<T>;
+
+	TempResourceAllocator<U8> m_alloc;
+	Header m_header;
+
+	MDArray<U8> m_verts;
+	MDArray<U8> m_indices;
+	MDArray<SubMesh> m_subMeshes;
+	U8 m_vertSize = 0;
+
+	Bool isLoaded() const
+	{
+		return m_verts.getSize() > 0;
+	}
+
+	ANKI_USE_RESULT Error loadInternal(const CString& filename);
+
+	static ANKI_USE_RESULT Error checkFormat(
+		const Format& fmt, 
+		const CString& attrib,
+		Bool cannotBeEmpty);
+};
+
+} // end namespace anki
+
+#endif
+

+ 288 - 0
src/resource/MeshLoader2.cpp

@@ -0,0 +1,288 @@
+// Copyright (C) 2014, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "anki/resource/MeshLoader2.h"
+#include "anki/util/File.h"
+
+namespace anki {
+
+//==============================================================================
+MeshLoader2::~MeshLoader2()
+{
+	// WARNING: Watch the order of deallocation. Reverse of the deallocation to
+	// have successful cleanups
+	m_verts.destroy(m_alloc);
+	m_indices.destroy(m_alloc);
+	m_subMeshes.destroy(m_alloc);
+}
+
+//==============================================================================
+Error MeshLoader2::load(
+	TempResourceAllocator<U8> alloc,
+	const CString& filename)
+{
+	m_alloc = alloc;
+	
+	Error err = loadInternal(filename);
+	if(err)
+	{
+		ANKI_LOGE("Failed to load mesh %s", &filename[0]);
+	}
+
+	return err;
+}
+
+//==============================================================================
+Error MeshLoader2::loadInternal(const CString& filename)
+{
+	Error err = ErrorCode::NONE;
+
+	File file;
+	err = file.open(filename, 
+		File::OpenFlag::READ | File::OpenFlag::BINARY 
+		| File::OpenFlag::LITTLE_ENDIAN);
+	if(err) return err;
+
+	// Load header
+	err = file.read(&m_header, sizeof(m_header));
+	if(err) return err;
+
+	//
+	// Check header
+	//
+	if(memcmp(&m_header.m_magic[0], "ANKIMES2", 8) != 0)
+	{
+		ANKI_LOGE("Wrong magic word");
+		return ErrorCode::USER_DATA;
+	}
+
+	if(checkFormat(m_header.m_positionsFormat, "positions", true)
+		|| checkFormat(m_header.m_normalsFormat, "normals", true)
+		|| checkFormat(m_header.m_tangentsFormat, "tangents", true)
+		|| checkFormat(m_header.m_colorsFormat, "colors", false)
+		|| checkFormat(m_header.m_uvsFormat, "UVs", true)
+		|| checkFormat(m_header.m_boneWeightsFormat, "bone weights", false)
+		|| checkFormat(m_header.m_boneIndicesFormat, "bone ids", false)
+		|| checkFormat(m_header.m_indicesFormat, "indices format", true))
+	{
+		return ErrorCode::USER_DATA;
+	}
+
+	// Check positions
+	if(m_header.m_positionsFormat.m_components != ComponentFormat::R32G32B32
+		|| m_header.m_positionsFormat.m_transform != FormatTransform::FLOAT)
+	{
+		ANKI_LOGE("Incorrect/unsupported positions format");
+		return ErrorCode::USER_DATA;
+	}
+
+	// Check normals
+	if(m_header.m_normalsFormat.m_components != ComponentFormat::R10G10B10A2
+		|| m_header.m_normalsFormat.m_transform != FormatTransform::SNORM)
+	{
+		ANKI_LOGE("Incorrect/unsupported normals format");
+		return ErrorCode::USER_DATA;
+	}
+
+	// Check tangents
+	if(m_header.m_tangentsFormat.m_components != ComponentFormat::R10G10B10A2
+		|| m_header.m_tangentsFormat.m_transform != FormatTransform::SNORM)
+	{
+		ANKI_LOGE("Incorrect/unsupported tangents format");
+		return ErrorCode::USER_DATA;
+	}
+
+	// Check colors
+	if(m_header.m_colorsFormat.m_components != ComponentFormat::NONE
+		|| m_header.m_colorsFormat.m_transform != FormatTransform::NONE)
+	{
+		ANKI_LOGE("Incorrect/unsupported color format");
+		return ErrorCode::USER_DATA;
+	}
+
+	// Check UVs
+	if(m_header.m_uvsFormat.m_components != ComponentFormat::R16G16
+		|| m_header.m_uvsFormat.m_transform != FormatTransform::FLOAT)
+	{
+		ANKI_LOGE("Incorrect/unsupported UVs format");
+		return ErrorCode::USER_DATA;
+	}
+
+	Bool hasBoneInfo = false;
+	if(m_header.m_boneWeightsFormat.m_components != ComponentFormat::NONE)
+	{
+		// Has bone info
+
+		hasBoneInfo = true;
+
+		// Bone weights
+		if(m_header.m_boneWeightsFormat.m_components 
+			!= ComponentFormat::R8G8B8A8
+			|| m_header.m_boneWeightsFormat.m_transform 
+			!= FormatTransform::UNORM)
+		{
+			ANKI_LOGE("Incorrect/unsupported UVs format");
+			return ErrorCode::USER_DATA;
+		}
+
+		// Bone indices
+		if(m_header.m_boneIndicesFormat.m_components 
+			!= ComponentFormat::R16G16B16A16
+			|| m_header.m_boneIndicesFormat.m_transform 
+			!= FormatTransform::UINT)
+		{
+			ANKI_LOGE("Incorrect/unsupported UVs format");
+			return ErrorCode::USER_DATA;
+		}
+	}
+	else
+	{
+		// No bone info
+
+		// Bone weights
+		if(m_header.m_boneWeightsFormat.m_components 
+			!= ComponentFormat::NONE
+			|| m_header.m_boneWeightsFormat.m_transform 
+			!= FormatTransform::NONE)
+		{
+			ANKI_LOGE("Incorrect/unsupported UVs format");
+			return ErrorCode::USER_DATA;
+		}
+
+		// Bone indices
+		if(m_header.m_boneIndicesFormat.m_components 
+			!= ComponentFormat::NONE
+			|| m_header.m_boneIndicesFormat.m_transform 
+			!= FormatTransform::NONE)
+		{
+			ANKI_LOGE("Incorrect/unsupported UVs format");
+			return ErrorCode::USER_DATA;
+		}
+	}
+
+	// Check indices
+	if(m_header.m_totalIndicesCount < 3
+		|| (m_header.m_totalIndicesCount % 3) != 0
+		|| m_header.m_totalIndicesCount > MAX_U16
+		|| m_header.m_indicesFormat.m_components != ComponentFormat::R16
+		|| m_header.m_indicesFormat.m_transform != FormatTransform::UINT)
+	{
+		// Only 16bit indices are supported for now
+		ANKI_LOGE("Incorrect/unsuported index info");
+		return ErrorCode::USER_DATA;
+	}
+
+	// Check other
+	if(m_header.m_totalVerticesCount == 0)
+	{
+		ANKI_LOGE("Incorrect/unsuported vertex count");
+		return ErrorCode::USER_DATA;
+	}
+
+	if(m_header.m_uvsChannelCount != 1)
+	{
+		ANKI_LOGE("Incorrect/unsuported UVs channel count");
+		return ErrorCode::USER_DATA;
+	}
+
+	if(m_header.m_subMeshCount == 0)
+	{
+		ANKI_LOGE("Incorrect/unsuported submesh count");
+		return ErrorCode::USER_DATA;
+	}
+
+	//
+	// Read submesh info
+	//
+	err = m_subMeshes.create(m_alloc, m_header.m_subMeshCount);
+	if(err) return err;
+	err = file.read(&m_subMeshes[0], m_subMeshes.getSizeInBytes());
+	if(err) return err;
+
+	// Checks
+	U idxSum = 0;
+	for(U i = 0; i < m_subMeshes.getSize(); i++)
+	{
+		const SubMesh& sm = m_subMeshes[0];
+		if(sm.m_firstIndex != idxSum
+			|| sm.m_indicesCount < 3)
+		{
+			ANKI_LOGE("Incorrect sub mesh info");
+			return ErrorCode::USER_DATA;
+		}
+
+		idxSum += sm.m_indicesCount;
+	}
+
+	if(idxSum != m_header.m_totalIndicesCount)
+	{
+		ANKI_LOGE("Incorrect sub mesh info");
+		return ErrorCode::USER_DATA;
+	}
+
+	//
+	// Read indices
+	//
+	err = m_indices.create(m_alloc, m_header.m_totalIndicesCount * sizeof(U16));
+	if(err) return err;
+	err = file.read(&m_indices[0], m_indices.getSizeInBytes());
+	if(err) return err;
+
+	//
+	// Read vertices
+	//
+	m_vertSize = 
+		3 * sizeof(F32) // pos
+		+ 1 * sizeof(U32) // norm
+		+ 1 * sizeof(U32) // tang
+		+ 2 * sizeof(U16) // uvs
+		+ (hasBoneInfo) ? (4 * sizeof(U8) + 4 * sizeof(U16)) : 0;
+
+	err = m_verts.create(m_alloc, m_header.m_totalVerticesCount * m_vertSize);
+	if(err) return err;
+	err = file.read(&m_verts[0], m_verts.getSizeInBytes());
+	if(err) return err;
+
+	return err;
+}
+
+//==============================================================================
+Error MeshLoader2::checkFormat(
+	const Format& fmt, 
+	const CString& attrib,
+	Bool cannotBeEmpty)
+{
+	if(fmt.m_components >= ComponentFormat::COUNT)
+	{
+		ANKI_LOGE("Incorrect component format for %s", &attrib[0]);
+		return ErrorCode::USER_DATA;
+	}
+
+	if(fmt.m_transform >= FormatTransform::COUNT)
+	{
+		ANKI_LOGE("Incorrect format transform for %s", &attrib[0]);
+		return ErrorCode::USER_DATA;
+	}
+
+	if(cannotBeEmpty)
+	{
+		if(fmt.m_components == ComponentFormat::NONE)
+		{
+			ANKI_LOGE("Format cannot be zero for %s", &attrib[0]);
+			return ErrorCode::USER_DATA;
+		}
+
+		if(fmt.m_transform == FormatTransform::NONE)
+		{
+			ANKI_LOGE("Transform cannot be zero for %s", &attrib[0]);
+			return ErrorCode::USER_DATA;
+		}
+	}
+
+	return ErrorCode::NONE;
+}
+
+} // end namespace anki
+

+ 1 - 1
tools/scene/CMakeLists.txt

@@ -2,5 +2,5 @@ include_directories("../../thirdparty/assimp/include")
 
 add_definitions("-fexceptions")
 
-add_executable(ankisceneimp Main.cpp Common.cpp Exporter.cpp)
+add_executable(ankisceneimp Main.cpp Common.cpp Exporter.cpp ExporterMesh.cpp)
 target_link_libraries(ankisceneimp ankiassimp) 

+ 4 - 148
tools/scene/Exporter.cpp

@@ -190,154 +190,6 @@ std::string Exporter::getModelName(const Model& model) const
 	return name;
 }
 
-//==============================================================================
-void Exporter::exportMesh(
-	const aiMesh& mesh, 
-	const aiMatrix4x4* transform) const
-{
-	std::string name = getMeshName(mesh);
-	std::fstream file;
-	LOGI("Exporting mesh %s", name.c_str());
-
-	uint32_t vertsCount = mesh.mNumVertices;
-
-	// Open file
-	file.open(m_outputDirectory + name + ".ankimesh",
-		std::ios::out | std::ios::binary);
-
-	// Write magic word
-	file.write("ANKIMESH", 8);
-
-	// Write the name
-	uint32_t size = name.size();
-	file.write((char*)&size, sizeof(uint32_t));
-	file.write(&name[0], size);
-
-	// Write positions
-	file.write((char*)&vertsCount, sizeof(uint32_t));
-	for(uint32_t i = 0; i < mesh.mNumVertices; i++)
-	{
-		aiVector3D pos = mesh.mVertices[i];
-
-		// Transform
-		if(transform)
-		{
-			pos = (*transform) * pos;
-		}
-
-		// flip 
-		if(m_flipyz)
-		{
-			static const aiMatrix4x4 toLefthanded(
-				1, 0, 0, 0, 
-				0, 0, 1, 0, 
-				0, -1, 0, 0, 
-				0, 0, 0, 1);
-
-			pos = toLefthanded * pos;
-		}
-
-		for(uint32_t j = 0; j < 3; j++)
-		{
-			file.write((char*)&pos[j], sizeof(float));
-		}
-	}
-
-	// Write the indices
-	file.write((char*)&mesh.mNumFaces, sizeof(uint32_t));
-	for(uint32_t i = 0; i < mesh.mNumFaces; i++)
-	{
-		const aiFace& face = mesh.mFaces[i];
-		
-		if(face.mNumIndices != 3)
-		{
-			ERROR("For some reason the assimp didn't triangulate");
-		}
-
-		for(uint32_t j = 0; j < 3; j++)
-		{
-			uint32_t index = face.mIndices[j];
-			file.write((char*)&index, sizeof(uint32_t));
-		}
-	}
-
-	// Write the tex coords
-	file.write((char*)&vertsCount, sizeof(uint32_t));
-
-	// For all channels
-	for(uint32_t ch = 0; ch < mesh.GetNumUVChannels(); ch++)
-	{
-		if(mesh.mNumUVComponents[ch] != 2)
-		{
-			ERROR("Incorrect number of UV components");
-		}
-
-		// For all tex coords of this channel
-		for(uint32_t i = 0; i < vertsCount; i++)
-		{
-			aiVector3D texCoord = mesh.mTextureCoords[ch][i];
-
-			for(uint32_t j = 0; j < 2; j++)
-			{
-				file.write((char*)&texCoord[j], sizeof(float));
-			}
-		}
-	}
-
-	// Write bone weigths count
-	if(mesh.HasBones())
-	{
-#if 0
-		// Write file
-		file.write((char*)&vertsCount, sizeof(uint32_t));
-
-		// Gather info for each vertex
-		std::vector<Vw> vw;
-		vw.resize(vertsCount);
-		memset(&vw[0], 0, sizeof(Vw) * vertsCount);
-
-		// For all bones
-		for(uint32_t i = 0; i < mesh.mNumBones; i++)
-		{
-			const aiBone& bone = *mesh.mBones[i];
-
-			// for every weights of the bone
-			for(uint32_t j = 0; j < bone.mWeightsCount; j++)
-			{
-				const aiVertexWeight& weigth = bone.mWeights[j];
-
-				// Sanity check
-				if(weight.mVertexId >= vertCount)
-				{
-					ERROR("Out of bounds vert ID");
-				}
-
-				Vm& a = vm[weight.mVertexId];
-
-				// Check out of bounds
-				if(a.bonesCount >= MAX_BONES_PER_VERTEX)
-				{
-					LOGW("Too many bones for vertex %d", weigth.mVertexId);
-					continue;
-				}
-
-				// Write to vertex
-				a.boneIds[a.bonesCount] = i;
-				a.weigths[a.bonesCount] = weigth.mWeigth;
-				++a.bonesCount;
-			}
-
-			// Now write the file
-		}
-#endif
-	}
-	else
-	{
-		uint32_t num = 0;
-		file.write((char*)&num, sizeof(uint32_t));
-	}
-}
-
 //==============================================================================
 void Exporter::exportSkeleton(const aiMesh& mesh) const
 {
@@ -957,6 +809,8 @@ void Exporter::load()
 {
 	LOGI("Loading file %s", &m_inputFilename[0]);
 
+	m_importer.SetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE, 170);
+
 	const aiScene* scene = m_importer.ReadFile(m_inputFilename, 0
 		//| aiProcess_FindInstances
 		| aiProcess_Triangulate
@@ -965,6 +819,8 @@ void Exporter::load()
 		| aiProcess_ImproveCacheLocality
 		| aiProcess_OptimizeMeshes
 		| aiProcess_RemoveRedundantMaterials
+		| aiProcess_CalcTangentSpace
+		| aiProcess_GenSmoothNormals
 		);
 
 	if(!scene)

+ 473 - 0
tools/scene/ExporterMesh.cpp

@@ -0,0 +1,473 @@
+// Copyright (C) 2014, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "Exporter.h"
+
+//==============================================================================
+enum class ComponentFormat: uint32_t
+{
+	NONE,
+
+	R8,
+	R8G8,
+	R8G8B8,
+	R8G8B8A8,
+
+	R16,
+	R16G16,
+	R16G16B16,
+	R16G16B16A16,
+
+	R32,
+	R32G32,
+	R32G32B32,
+	R32G32B32A32,
+
+	R10G10B10A2,
+
+	COUNT
+};
+
+enum class FormatTransform: uint32_t
+{
+	NONE,
+
+	UNORM,
+	SNORM,
+	UINT,
+	SINT,
+	FLOAT,
+
+	COUNT
+};
+
+struct Format
+{
+	ComponentFormat m_components = ComponentFormat::NONE;
+	FormatTransform m_transform = FormatTransform::NONE;
+};
+
+struct Header
+{
+	char m_magic[8]; ///< Magic word.
+	uint32_t m_flags;
+	uint32_t m_flags2;
+	Format m_positionsFormat;
+	Format m_normalsFormat;
+	Format m_tangentsFormat;
+	Format m_colorsFormat; ///< Vertex color.
+	Format m_uvsFormat;
+	Format m_boneWeightsFormat;
+	Format m_boneIndicesFormat;
+	Format m_indicesFormat; ///< Vertex indices.
+
+	uint32_t m_totalIndicesCount;
+	uint32_t m_totalVerticesCount;
+	uint32_t m_uvsChannelCount;
+	uint32_t m_subMeshCount;
+
+	uint8_t m_padding[32];
+};
+
+struct SubMesh
+{
+	uint32_t m_firstIndex = 0;
+	uint32_t m_indicesCount = 0;
+};
+
+struct Vertex
+{
+	float m_position[3];
+	uint32_t m_normal;
+	uint32_t m_tangent;
+	uint16_t m_uv[2];
+};
+
+//==============================================================================
+static uint16_t toF16(float f)
+{
+	union Val32
+	{
+		int32_t i;
+		float f;
+		uint32_t u;
+	};
+
+	uint16_t out;
+	Val32 v32;
+	v32.f = f;
+	int32_t i = v32.i;
+	int32_t s = (i >> 16) & 0x00008000;
+	int32_t e = ((i >> 23) & 0x000000ff) - (127 - 15);
+	int32_t m = i & 0x007fffff;
+
+	if(e <= 0)
+	{
+		if(e < -10)
+		{
+			out = 0;
+		}
+		else
+		{
+			m = (m | 0x00800000) >> (1 - e);
+
+			if(m & 0x00001000) 
+			{
+				m += 0x00002000;
+			}
+
+			out = s | (m >> 13);
+		}
+	}
+	else if(e == 0xff - (127 - 15))
+	{
+		if(m == 0)
+		{
+			out = s | 0x7c00;
+		}
+		else
+		{
+			m >>= 13;
+			out = s | 0x7c00 | m | (m == 0);
+		}
+	}
+	else
+	{
+		if(m &  0x00001000)
+		{
+			m += 0x00002000;
+
+			if(m & 0x00800000)
+			{
+				m =  0;
+				e += 1;
+			}
+		}
+
+		if (e > 30)
+		{
+			assert(0 && "Overflow");
+			out = s | 0x7c00;
+		}
+		else
+		{
+			out = s | (e << 10) | (m >> 13);
+		}
+	}
+
+	return out;
+}
+
+//==============================================================================
+uint32_t toR10G10B10A2Sint(float r, float g, float b, float a)
+{
+	uint32_t x = pow(2, 11 - 1) - 1;
+
+	uint32_t ur = r * x;
+	uint32_t ug = g * x;
+	uint32_t ub = r * b;
+	uint32_t ua = a * (pow(2, 2 - 1) - 1);
+
+	uint32_t out = ua << 30;
+	out |= ub << 20;
+	out |= ug << 10;
+	out |= ur << 0;
+
+	return out;
+}
+
+//==============================================================================
+void Exporter::exportMesh(
+	const aiMesh& mesh, 
+	const aiMatrix4x4* transform) const
+{
+#if 0
+	std::string name = getMeshName(mesh);
+	std::fstream file;
+	LOGI("Exporting mesh %s", name.c_str());
+
+	uint32_t vertsCount = mesh.mNumVertices;
+
+	// Open file
+	file.open(m_outputDirectory + name + ".ankimesh",
+		std::ios::out | std::ios::binary);
+
+	// Write magic word
+	file.write("ANKIMESH", 8);
+
+	// Write the name
+	uint32_t size = name.size();
+	file.write((char*)&size, sizeof(uint32_t));
+	file.write(&name[0], size);
+
+	// Write positions
+	file.write((char*)&vertsCount, sizeof(uint32_t));
+	for(uint32_t i = 0; i < mesh.mNumVertices; i++)
+	{
+		aiVector3D pos = mesh.mVertices[i];
+
+		// Transform
+		if(transform)
+		{
+			pos = (*transform) * pos;
+		}
+
+		// flip 
+		if(m_flipyz)
+		{
+			static const aiMatrix4x4 toLefthanded(
+				1, 0, 0, 0, 
+				0, 0, 1, 0, 
+				0, -1, 0, 0, 
+				0, 0, 0, 1);
+
+			pos = toLefthanded * pos;
+		}
+
+		for(uint32_t j = 0; j < 3; j++)
+		{
+			file.write((char*)&pos[j], sizeof(float));
+		}
+	}
+
+	// Write the indices
+	file.write((char*)&mesh.mNumFaces, sizeof(uint32_t));
+	for(uint32_t i = 0; i < mesh.mNumFaces; i++)
+	{
+		const aiFace& face = mesh.mFaces[i];
+		
+		if(face.mNumIndices != 3)
+		{
+			ERROR("For some reason the assimp didn't triangulate");
+		}
+
+		for(uint32_t j = 0; j < 3; j++)
+		{
+			uint32_t index = face.mIndices[j];
+			file.write((char*)&index, sizeof(uint32_t));
+		}
+	}
+
+	// Write the tex coords
+	file.write((char*)&vertsCount, sizeof(uint32_t));
+
+	// For all channels
+	for(uint32_t ch = 0; ch < mesh.GetNumUVChannels(); ch++)
+	{
+		if(mesh.mNumUVComponents[ch] != 2)
+		{
+			ERROR("Incorrect number of UV components");
+		}
+
+		// For all tex coords of this channel
+		for(uint32_t i = 0; i < vertsCount; i++)
+		{
+			aiVector3D texCoord = mesh.mTextureCoords[ch][i];
+
+			for(uint32_t j = 0; j < 2; j++)
+			{
+				file.write((char*)&texCoord[j], sizeof(float));
+			}
+		}
+	}
+
+	// Write bone weigths count
+	if(mesh.HasBones())
+	{
+#if 0
+		// Write file
+		file.write((char*)&vertsCount, sizeof(uint32_t));
+
+		// Gather info for each vertex
+		std::vector<Vw> vw;
+		vw.resize(vertsCount);
+		memset(&vw[0], 0, sizeof(Vw) * vertsCount);
+
+		// For all bones
+		for(uint32_t i = 0; i < mesh.mNumBones; i++)
+		{
+			const aiBone& bone = *mesh.mBones[i];
+
+			// for every weights of the bone
+			for(uint32_t j = 0; j < bone.mWeightsCount; j++)
+			{
+				const aiVertexWeight& weigth = bone.mWeights[j];
+
+				// Sanity check
+				if(weight.mVertexId >= vertCount)
+				{
+					ERROR("Out of bounds vert ID");
+				}
+
+				Vm& a = vm[weight.mVertexId];
+
+				// Check out of bounds
+				if(a.bonesCount >= MAX_BONES_PER_VERTEX)
+				{
+					LOGW("Too many bones for vertex %d", weigth.mVertexId);
+					continue;
+				}
+
+				// Write to vertex
+				a.boneIds[a.bonesCount] = i;
+				a.weigths[a.bonesCount] = weigth.mWeigth;
+				++a.bonesCount;
+			}
+
+			// Now write the file
+		}
+#endif
+	}
+	else
+	{
+		uint32_t num = 0;
+		file.write((char*)&num, sizeof(uint32_t));
+	}
+#endif
+
+	std::string name = mesh.mName.C_Str();
+	std::fstream file;
+	LOGI("Exporting mesh %s", name.c_str());
+
+	// Open file
+	file.open(m_outputDirectory + name + ".ankimesh",
+		std::ios::out | std::ios::binary);
+
+	Header header;
+	memset(&header, 0, sizeof(header));
+
+	// Checks
+	if(mesh.mNumFaces == 0)
+	{
+		ERROR("Incorrect face number");
+	}
+
+	if(mesh.mVertices == 0)
+	{
+		ERROR("Incorrect vertex count number");
+	}
+
+	if(!mesh.HasPositions()
+		|| !mesh.HasNormals()
+		|| !mesh.HasTangentsAndBitangents()
+		|| !mesh.HasTextureCoords(0))
+	{
+		ERROR("Missing attribute");
+	}
+	
+	// Write header
+	static const char* magic = "ANKIMES2";
+	memcpy(&header.m_magic, magic, 8);
+	
+	header.m_positionsFormat.m_components = ComponentFormat::R32G32B32;
+	header.m_positionsFormat.m_transform = FormatTransform::FLOAT;
+
+	header.m_normalsFormat.m_components = ComponentFormat::R10G10B10A2;
+	header.m_normalsFormat.m_transform = FormatTransform::SNORM;
+
+	header.m_tangentsFormat.m_components = ComponentFormat::R10G10B10A2;
+	header.m_tangentsFormat.m_transform = FormatTransform::SNORM;
+
+	header.m_uvsFormat.m_components = ComponentFormat::R16G16;
+	header.m_uvsFormat.m_transform = FormatTransform::FLOAT;
+
+	header.m_indicesFormat.m_components = ComponentFormat::R16;
+	header.m_indicesFormat.m_transform = FormatTransform::UINT;
+
+	header.m_totalIndicesCount = mesh.mNumFaces * 3;
+	header.m_totalVerticesCount = mesh.mNumVertices;
+	header.m_uvsChannelCount = 1;
+	header.m_subMeshCount = 1;
+
+	file.write(reinterpret_cast<char*>(&header), sizeof(header));
+
+	// Write sub meshes
+	SubMesh smesh;
+	smesh.m_firstIndex = 0;
+	smesh.m_indicesCount = header.m_totalIndicesCount;
+	file.write(reinterpret_cast<char*>(&smesh), sizeof(smesh));
+
+	// Write indices
+	for(unsigned i = 0; i < mesh.mNumFaces; i++)
+	{
+		const aiFace& face = mesh.mFaces[i];
+		
+		if(face.mNumIndices != 3)
+		{
+			ERROR("For some reason the assimp didn't triangulate");
+		}
+
+		for(unsigned j = 0; j < 3; j++)
+		{
+			uint32_t index32 = face.mIndices[j];
+			if(index32 > 0xFFFF)
+			{
+				ERROR("Index too big");
+			}
+
+			uint16_t index = index32;
+			file.write(reinterpret_cast<char*>(&index), sizeof(index));
+		}
+	}
+
+	// Write vertices
+	aiMatrix3x3 normalMat;
+	if(transform)
+	{
+		normalMat = aiMatrix3x3(*transform);
+	}
+
+	for(unsigned i = 0; i < mesh.mNumVertices; i++)
+	{
+		aiVector3D pos = mesh.mVertices[i];
+		aiVector3D n = mesh.mNormals[i];
+		aiVector3D t = mesh.mTangents[i];
+		aiVector3D b = mesh.mBitangents[i];
+		const aiVector3D& uv = mesh.mTextureCoords[0][i];
+
+		if(transform)
+		{
+			pos = (*transform) * pos;
+			n = normalMat * n;
+			t = normalMat * t;
+			b = normalMat * b;
+		}
+
+		if(m_flipyz)
+		{
+			static const aiMatrix4x4 toLefthanded(
+				1, 0, 0, 0, 
+				0, 0, 1, 0, 
+				0, -1, 0, 0, 
+				0, 0, 0, 1);
+
+			pos = toLefthanded * pos;
+			n = toLefthanded * n;
+			t = toLefthanded * t;
+			b = toLefthanded * b;
+		}
+
+		Vertex vert;
+
+		// Position
+		vert.m_position[0] = pos[0];
+		vert.m_position[1] = pos[1];
+		vert.m_position[2] = pos[2];
+
+		// Normal
+		vert.m_normal = toR10G10B10A2Sint(n[0], n[1], n[2], 0.0);
+
+		// Tangent
+		float w = ((n ^ t) * b < 0.0) ? 1.0 : -1.0;
+		vert.m_tangent = toR10G10B10A2Sint(t[0], t[1], t[2], w);
+
+		// Tex coords
+		vert.m_uv[0] = toF16(uv[0]);
+		vert.m_uv[1] = toF16(uv[1]);
+
+		// Write
+		file.write(reinterpret_cast<char*>(&vert), sizeof(vert));
+	}
+}