Index: trunk/src/formats/md5_loader.cc
===================================================================
--- trunk/src/formats/md5_loader.cc	(revision 190)
+++ trunk/src/formats/md5_loader.cc	(revision 190)
@@ -0,0 +1,287 @@
+// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+#include "nv/formats/md5_loader.hh"
+
+#include <glm/gtc/constants.hpp>
+#include <glm/gtx/string_cast.hpp>
+#include "nv/logging.hh"
+#include "nv/io/std_stream.hh"
+#include <cstring>
+
+using namespace nv;
+
+static void next_line( std::istream& stream )
+{
+	stream.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
+}
+
+static inline void discard( std::istream& stream, const std::string& token )
+{
+	std::string discarded;
+	stream >> discarded;
+	assert( discarded == token );
+}
+
+
+static void remove_quotes( std::string& str )
+{
+	size_t n;
+	while ( ( n = str.find('\"') ) != std::string::npos ) str.erase(n,1);
+}
+
+void unit_quat_w( glm::quat& quat )
+{
+	float t = 1.0f - ( quat.x * quat.x ) - ( quat.y * quat.y ) - ( quat.z * quat.z );
+	quat.w = ( t < 0.0f ? 0.0f : -sqrtf(t) );
+}
+
+bool md5_loader::load( stream& source )
+{
+	std_stream sstream( &source );
+	std::string command;
+
+	sstream >> command;
+	while ( !sstream.eof() )
+	{
+		if ( command == "MD5Version" )
+		{
+			sstream >> m_md5_version;
+			assert( m_md5_version == 10 );
+		}
+		else if ( command == "commandline" )
+		{
+			next_line( sstream ); 
+		}
+		else if ( command == "numJoints" )
+		{
+			sstream >> m_num_joints;
+			m_joints.reserve( m_num_joints );
+		}
+		else if ( command == "numMeshes" )
+		{
+			sstream >> m_num_meshes;
+			m_meshes.reserve( m_num_meshes );
+		}
+		else if ( command == "joints" )
+		{
+			discard( sstream, "{" );
+			md5_joint joint;
+			for ( int i = 0; i < m_num_joints; ++i )
+			{
+				sstream >> joint.name >> joint.parent_id;
+				discard( sstream, "(" );
+				sstream >> joint.pos.x >> joint.pos.y >> joint.pos.z;
+				discard( sstream, ")" );
+				discard( sstream, "(" );
+				sstream >> joint.orient.x >> joint.orient.y >> joint.orient.z;
+				remove_quotes( joint.name );
+				unit_quat_w( joint.orient );
+				m_joints.push_back( joint );
+				next_line( sstream );
+			}
+			discard( sstream, "}" );
+		}
+		else if ( command == "mesh" )
+		{
+			// TODO : efficiency dammit
+			md5_mesh mesh;
+			int num_verts, num_tris, num_weights;
+
+			discard( sstream, "{" );
+			sstream >> command;
+			while ( command != "}" ) 
+			{
+				if ( command == "shader" )
+				{
+					sstream >> mesh.shader;
+					remove_quotes( mesh.shader );
+					// texturePath.replace_extension( ".tga" );
+					next_line( sstream );
+				}
+				else if ( command == "numverts")
+				{
+					sstream >> num_verts; 
+					next_line( sstream );
+					for ( int i = 0; i < num_verts; ++i )
+					{
+						md5_vertex vert;
+						std::string ignore;
+						discard( sstream, "vert" );
+						sstream >> ignore;
+						discard( sstream, "(" );
+						sstream >> vert.texcoord.x >> vert.texcoord.y;
+						discard( sstream, ")" );
+						sstream >> vert.start_weight >> vert.weight_count;
+						next_line( sstream );
+
+						mesh.verts.push_back(vert);
+						mesh.texcoord_buffer.push_back( vert.texcoord );
+					}  
+				}
+				else if ( command == "numtris" )
+				{
+					sstream >> num_tris;
+					next_line( sstream );
+					for ( int i = 0; i < num_tris; ++i )
+					{
+						md5_triangle tri;
+						std::string ignore;
+						discard( sstream, "tri" );
+						sstream >> ignore >> tri.indices[0] >> tri.indices[1] >> tri.indices[2];
+						next_line( sstream );
+
+						mesh.tris.push_back( tri );
+						mesh.index_buffer.push_back( (uint32)tri.indices[0] );
+						mesh.index_buffer.push_back( (uint32)tri.indices[1] );
+						mesh.index_buffer.push_back( (uint32)tri.indices[2] );
+					}              
+				}
+				else if ( command == "numweights" )
+				{
+					sstream >> num_weights;
+					next_line( sstream );
+					for ( int i = 0; i < num_weights; ++i )
+					{
+						md5_weight weight;
+						std::string ignore;
+						discard( sstream, "weight" );
+						sstream >> ignore >> weight.joint_id >> weight.bias;
+						discard( sstream, "(" );
+						sstream >> weight.pos.x >> weight.pos.y >> weight.pos.z;
+						discard( sstream, ")" );
+						next_line( sstream );
+						mesh.weights.push_back(weight);
+					}
+				}
+				else
+				{
+					next_line( sstream );
+				}
+
+				sstream >> command;
+			}
+
+			prepare_mesh( mesh );
+			prepare_normals( mesh );
+
+			m_meshes.push_back(mesh);
+		}
+		sstream >> command;
+	}
+
+	assert( m_joints.size() == m_num_joints );
+	assert( m_meshes.size() == m_num_meshes );
+	return true;
+}
+
+bool md5_loader::prepare_mesh( md5_mesh& mesh )
+{
+	mesh.position_buffer.clear();
+	mesh.texcoord_buffer.clear();
+
+	for ( uint32 i = 0; i < mesh.verts.size(); ++i )
+	{
+		md5_vertex& vert = mesh.verts[i];
+
+		vert.position = glm::vec3(0);
+		vert.normal   = glm::vec3(0);
+		vert.tangent  = glm::vec3(0);
+
+		for ( int j = 0; j < vert.weight_count; ++j )
+		{
+			md5_weight& weight = mesh.weights[vert.start_weight + j];
+			md5_joint&  joint  = m_joints[weight.joint_id];
+
+			glm::vec3 rot_pos = joint.orient * weight.pos;
+
+			vert.position += ( joint.pos + rot_pos ) * weight.bias;
+		}
+
+		mesh.position_buffer.push_back(vert.position);
+		mesh.texcoord_buffer.push_back(vert.texcoord);
+	}
+
+	return true;
+}
+
+bool md5_loader::prepare_normals( md5_mesh& mesh )
+{
+	mesh.normal_buffer.clear();
+
+	for ( unsigned int i = 0; i < mesh.tris.size(); ++i )
+	{
+		const md5_triangle& tri = mesh.tris[i];
+		glm::vec3 v1 = mesh.verts[ tri.indices[0] ].position;
+		glm::vec3 v2 = mesh.verts[ tri.indices[1] ].position;
+		glm::vec3 v3 = mesh.verts[ tri.indices[2] ].position;
+		glm::vec3 xyz1 = v3 - v1;
+		glm::vec3 xyz2 = v2 - v1;
+
+		glm::vec3 normal = glm::cross( xyz1, xyz2 );
+
+		mesh.verts[ tri.indices[0] ].normal += normal;
+		mesh.verts[ tri.indices[1] ].normal += normal;
+		mesh.verts[ tri.indices[2] ].normal += normal;
+
+		const vec2& w1 = mesh.verts[ tri.indices[0] ].texcoord;
+		const vec2& w2 = mesh.verts[ tri.indices[1] ].texcoord;
+		const vec2& w3 = mesh.verts[ tri.indices[2] ].texcoord;
+
+		vec2 st1 = w3 - w1;
+		vec2 st2 = w2 - w1;
+
+		float coef = 1.0f / (st1.x * st2.y - st2.x * st1.y);
+
+		vec3 tangent = (( xyz1 * st2.y ) - ( xyz2 * st1.y )) * coef;
+
+		mesh.verts[ tri.indices[0] ].tangent += tangent;
+		mesh.verts[ tri.indices[1] ].tangent += tangent;
+		mesh.verts[ tri.indices[2] ].tangent += tangent;
+	}
+
+	for ( unsigned int i = 0; i < mesh.verts.size(); ++i )
+	{
+		md5_vertex& vert = mesh.verts[i];
+
+		glm::vec3 normal  = glm::normalize( vert.normal );
+		glm::vec3 tangent = glm::normalize( vert.tangent );
+		mesh.normal_buffer.push_back( normal );
+		mesh.tangent_buffer.push_back( tangent );
+
+		vert.normal  = glm::vec3(0);
+		vert.tangent = glm::vec3(0);
+
+		for ( int j = 0; j < vert.weight_count; ++j )
+		{
+			const md5_weight& weight = mesh.weights[vert.start_weight + j];
+			const md5_joint&  joint  = m_joints[weight.joint_id];
+			vert.normal  += ( normal  * joint.orient ) * weight.bias;
+			vert.tangent += ( tangent * joint.orient ) * weight.bias;
+		}
+	}
+
+	return true;
+}
+
+mesh* md5_loader::release_mesh()
+{
+	mesh* m = new mesh();
+	auto position = m->add_attribute< vec3 >( "nv_position" );
+	auto normal   = m->add_attribute< vec3 >( "nv_normal" );
+	auto texcoord = m->add_attribute< vec2 >( "nv_texcoord" );
+	auto tangent  = m->add_attribute< vec2 >( "nv_tangent" );
+	auto indices  = m->add_indices< uint32 >();
+
+	position->get().assign( m_meshes[0].position_buffer.begin(), m_meshes[0].position_buffer.end() );
+	normal  ->get().assign( m_meshes[0].normal_buffer.begin(),   m_meshes[0].normal_buffer.end() );
+	texcoord->get().assign( m_meshes[0].texcoord_buffer.begin(), m_meshes[0].texcoord_buffer.end() );
+	tangent ->get().assign( m_meshes[0].tangent_buffer.begin(),  m_meshes[0].tangent_buffer.end() );
+	indices ->get().assign( m_meshes[0].index_buffer.begin(),    m_meshes[0].index_buffer.end() );
+
+	m_size = m_meshes[0].index_buffer.size();
+	return m;
+}
