Index: /trunk/nv/formats/md5_loader.hh
===================================================================
--- /trunk/nv/formats/md5_loader.hh	(revision 258)
+++ /trunk/nv/formats/md5_loader.hh	(revision 259)
@@ -36,15 +36,9 @@
 	struct md5_vtx_data
 	{
-		glm::vec3 normal;
-		glm::vec3 tangent;
-		size_t    start_weight;
-		size_t    weight_count;
-	};
-
-	struct md5_weight
-	{
-		size_t    joint_id;
-		float     bias;
-		glm::vec3 pos;
+		vec3  position;
+		vec3  normal;
+		vec3  tangent;
+		ivec4 boneindex;
+		vec4  boneweight;
 	};
 
@@ -108,8 +102,9 @@
 		md5_mesh_instance( const md5_mesh_data* a_data );
 
-		uint32               m_size;
-		uint32               m_indices;
-		md5_vtx_pnt*         m_pntdata;
-		const md5_mesh_data* m_data;
+		uint32                   m_size;
+		uint32                   m_indices;
+		md5_vtx_pnt*             m_pntdata;
+		std::vector< transform > m_pos_offset;
+		const md5_mesh_data*     m_data;
 	};
 
@@ -125,6 +120,6 @@
 		md5_vtx_pnt*                m_pntdata;
 		std::string                 m_shader;
+		std::vector< transform >    m_bone_offset;
 		std::vector< md5_vtx_data > m_vtx_data;
-		std::vector< md5_weight   > m_weights;
 	};
 
@@ -139,4 +134,16 @@
 		virtual size_t get_mesh_count() const { return m_meshes.size(); }
 	protected:
+		struct md5_weight
+		{
+			size_t    joint_id;
+			float     bias;
+			glm::vec3 pos;
+		};
+
+		struct md5_weight_info
+		{
+			size_t     start_weight;
+			size_t     weight_count;
+		};
 
 		struct md5_joint
@@ -147,5 +154,5 @@
 		};
 	protected:
-		bool prepare_mesh( md5_mesh_data* mdata );
+		bool prepare_mesh( md5_mesh_data* mdata, std::vector< md5_weight >& weights, std::vector< md5_weight_info >& weight_info );
 	protected:
 		uint32 m_md5_version;
Index: /trunk/nv/transform.hh
===================================================================
--- /trunk/nv/transform.hh	(revision 258)
+++ /trunk/nv/transform.hh	(revision 259)
@@ -57,8 +57,39 @@
 			return result;
 		}
+		transform inverse() const
+		{
+			quat new_orient( glm::inverse( m_orientation ) );
+			// TODO: simplify
+			return transform( -glm::mat3_cast(new_orient) * m_position, new_orient );
+		}
+
+		transform& operator*=(const transform& rhs)
+		{
+			m_position = m_position + m_orientation * rhs.m_position;
+			m_orientation = m_orientation * rhs.m_orientation;
+			return *this;
+		}
+
+		vec3 transformed( const vec3 v ) const
+		{
+			return m_orientation * v + m_position;
+		}
 	private:
 		vec3 m_position;
 		quat m_orientation;
 	};
+
+	inline transform operator*(transform lhs, const transform& rhs)
+	{
+		lhs *= rhs;
+		return lhs;
+	}
+
+	inline vec3 operator*(const vec3 lhs, const transform& rhs)
+	{
+		return rhs.transformed( lhs );
+	}
+
+
 
 	template<>
Index: /trunk/src/formats/md5_loader.cc
===================================================================
--- /trunk/src/formats/md5_loader.cc	(revision 258)
+++ /trunk/src/formats/md5_loader.cc	(revision 259)
@@ -47,4 +47,6 @@
 	std_stream sstream( &source );
 	std::string command;
+	std::vector< md5_weight > weights;
+	std::vector< md5_weight_info > weight_info;
 
 	sstream >> command;
@@ -120,4 +122,5 @@
 					}
 					mesh->m_vtx_data.resize( num_verts );
+					weight_info.resize( num_verts );
 
 					next_line( sstream );
@@ -125,5 +128,4 @@
 					for ( int i = 0; i < num_verts; ++i )
 					{
-						md5_vtx_data& vdata = mesh->m_vtx_data[i];
 						size_t weight_count;
 						size_t start_weight;
@@ -132,6 +134,6 @@
 						std::getline( sstream, line );
 						sscanf( line.c_str(), "%*s %*u ( %f %f ) %u %u", &(texcoord.x), &(texcoord.y), &(start_weight), &(weight_count) );
-						vdata.start_weight = start_weight;
-						vdata.weight_count = weight_count;
+						weight_info[i].start_weight = start_weight;
+						weight_info[i].weight_count = weight_count;
 						mesh->m_tdata[i].texcoord = texcoord;
 					}  
@@ -166,5 +168,5 @@
 				{
 					sstream >> num_weights;
-					mesh->m_weights.reserve( num_weights );
+					weights.reserve( num_weights );
 					next_line( sstream );
 					std::string line;
@@ -175,5 +177,5 @@
 						std::getline( sstream, line );
 						sscanf( line.c_str(), "%*s %*u %u %f ( %f %f %f )", &(weight.joint_id), &(weight.bias), &(weight.pos.x), &(weight.pos.y), &(weight.pos.z));
- 						mesh->m_weights.push_back(weight);
+ 						weights.push_back(weight);
 					}
 				}
@@ -186,5 +188,5 @@
 			}
 
-			prepare_mesh( mesh );
+			prepare_mesh( mesh, weights, weight_info );
 
 			m_meshes.push_back(mesh);
@@ -198,11 +200,19 @@
 }
 
-bool md5_loader::prepare_mesh( md5_mesh_data* mdata )
+bool md5_loader::prepare_mesh( md5_mesh_data* mdata, std::vector< md5_weight >& weights, std::vector< md5_weight_info >& weight_info )
 {
 	uint32 vtx_count = mdata->m_vtx_data.size();
 	md5_vtx_pnt* vtcs = mdata->m_pntdata;
 
+	for ( auto joint : m_joints )
+	{
+		transform j( joint.pos, joint.orient );
+		mdata->m_bone_offset.push_back(j.inverse());
+	}
+
 	for ( uint32 i = 0; i < vtx_count; ++i )
 	{
+		size_t start_weight = weight_info[i].start_weight;
+		size_t weight_count = weight_info[i].weight_count;
 		md5_vtx_data& vdata = mdata->m_vtx_data[i];
 		md5_vtx_pnt& vtc = vtcs[i];
@@ -212,30 +222,45 @@
 		vtc.tangent  = glm::vec3(0);
 
-		std::sort( mdata->m_weights.begin() + vdata.start_weight, mdata->m_weights.begin() + vdata.start_weight + vdata.weight_count, [](const md5_weight& a, const md5_weight& b) -> bool { return a.bias > b.bias; } );
-
-		if ( vdata.weight_count > 4 )
+		std::sort( weights.begin() + start_weight, weights.begin() + start_weight + weight_count, [](const md5_weight& a, const md5_weight& b) -> bool { return a.bias > b.bias; } );
+
+		if ( weight_count > 4 )
 		{
 			float sum = 0.0f;
 			for ( size_t j = 0; j < 4; ++j )
 			{
-				sum += mdata->m_weights[vdata.start_weight + j].bias;
+				sum += weights[start_weight + j].bias;
 			}
 			float ratio = 1.0f / sum;
 			for ( size_t j = 0; j < 4; ++j )
 			{
-				mdata->m_weights[vdata.start_weight + j].bias = 
-					ratio * mdata->m_weights[vdata.start_weight + j].bias;
-			}
-			vdata.weight_count = 4;
-		}
-
-		for ( size_t j = 0; j < vdata.weight_count; ++j )
-		{
-			md5_weight& weight = mdata->m_weights[vdata.start_weight + j];
-			md5_joint&  joint  = m_joints[weight.joint_id];
-
-			glm::vec3 rot_pos = joint.orient * weight.pos;
-
-			vtc.position += ( joint.pos + rot_pos ) * weight.bias;
+				weights[start_weight + j].bias = ratio * weights[start_weight + j].bias;
+			}
+			weight_count = 4;
+		}
+
+		for ( size_t j = 0; j < 4; ++j )
+		{
+			if ( j < weight_count )
+			{
+				vdata.boneindex[j]  = weights[start_weight + j].joint_id;
+				vdata.boneweight[j] = weights[start_weight + j].bias;
+			}
+			else
+			{
+				vdata.boneindex[j]  = 0;
+				vdata.boneweight[j] = 0.0f;
+			}
+		}
+
+		for ( size_t j = 0; j < 4; ++j )
+		{
+			if ( j < weight_count )
+			{
+				md5_weight& weight = weights[start_weight + j];
+				md5_joint&  joint  = m_joints[weight.joint_id];
+				glm::vec3 rot_pos = joint.orient * weight.pos;
+
+				vtc.position += ( joint.pos + rot_pos ) * weight.bias;
+			}
 		}
 	}
@@ -286,13 +311,13 @@
 		vtcs[i].tangent  = tangent;
 
- 		vdata.normal  = glm::vec3(0);
- 		vdata.tangent = glm::vec3(0);
+		vdata.position = vtcs[i].position;
+		vdata.normal   = glm::vec3(0);
+ 		vdata.tangent  = glm::vec3(0);
  
- 		for ( size_t j = 0; j < vdata.weight_count; ++j )
+ 		for ( size_t j = 0; j < 4; ++j )
  		{
- 			const md5_weight& weight = mdata->m_weights[vdata.start_weight + j];
- 			const md5_joint&  joint  = m_joints[weight.joint_id];
- 			vdata.normal  += ( normal  * joint.orient ) * weight.bias;
- 			vdata.tangent += ( tangent * joint.orient ) * weight.bias;
+ 			const md5_joint&  joint  = m_joints[vdata.boneindex[j]];
+ 			vdata.normal  += ( normal  * joint.orient ) * vdata.boneweight[j];
+ 			vdata.tangent += ( tangent * joint.orient ) * vdata.boneweight[j];
  		}
 	}
@@ -537,4 +562,10 @@
 {
 	NV_PROFILE("md5::apply");
+	m_pos_offset.resize( skeleton.size() );
+	for ( unsigned int i = 0; i < skeleton.size(); ++i )
+	{
+		m_pos_offset[i] = skeleton[i] * m_data->m_bone_offset[i];
+	}
+
 	char* fill_ptr = (char*)&(m_pntdata[0]);
 	std::fill( fill_ptr, fill_ptr + m_size * ( sizeof( md5_vtx_pnt ) ), 0 );
@@ -544,15 +575,14 @@
 		md5_vtx_pnt& result = m_pntdata[i];
 
-		for ( size_t j = 0; j < vert.weight_count; ++j )
-		{
-			const md5_weight& weight = m_data->m_weights[vert.start_weight + j];
-			const transform& joint = skeleton[weight.joint_id];
-
-			glm::vec3 rot_pos = joint.get_orientation() * weight.pos;
-			result.position += ( joint.get_position() + rot_pos ) * weight.bias;
-
-			result.normal  += ( joint.get_orientation() * vert.normal  ) * weight.bias;
-			result.tangent += ( joint.get_orientation() * vert.tangent ) * weight.bias;
-		}
-	}
-}
+		for ( size_t j = 0; j < 4; ++j )
+		{
+			int   index  = vert.boneindex[j];
+			float weight = vert.boneweight[j];
+			transform joint  = skeleton[index];
+			transform offset = m_pos_offset[index];
+			result.position += offset.transformed( vert.position )        * weight;
+			result.normal   += ( joint.get_orientation() * vert.normal  ) * weight;
+			result.tangent  += ( joint.get_orientation() * vert.tangent ) * weight;
+		}
+	}
+}
