Index: trunk/src/formats/md3_loader.cc
===================================================================
--- trunk/src/formats/md3_loader.cc	(revision 148)
+++ trunk/src/formats/md3_loader.cc	(revision 149)
@@ -100,5 +100,5 @@
 	sint16 y;
 	sint16 z;
-	sint16 normal;
+	uint16 normal;
 };
 
@@ -118,4 +118,6 @@
 	md3_tag_t*     tags;
 	md3_surface_t* surfaces;
+	// extra information (not in md3 file)
+	sint32         vertices_per_frame;
 };
 
@@ -208,4 +210,5 @@
 
 	source.seek( md3->header.ofs_surfaces, origin::SET );
+	md3->vertices_per_frame = 0;
 
 	for ( sint32 i = 0; i < md3->header.num_surfaces; ++i )
@@ -213,4 +216,5 @@
 		if ( !read_surface( md3->surfaces + i, source ) ) return false;
 		if ( md3->header.num_frames != md3->surfaces[i].header.num_frames ) return false;
+		md3->vertices_per_frame += md3->surfaces[i].header.num_verts;
 	}
 	return true;
@@ -228,29 +232,35 @@
 }
 
-static inline vec3 md3_vec3( const md3_vertex_t& v )
-{
-//	return vec3( v.x * MD3_XYZ_SCALE, v.y * MD3_XYZ_SCALE, v.z * MD3_XYZ_SCALE );
-	return vec3( v.x * MD3_XYZ_SCALE, v.z * MD3_XYZ_SCALE, v.y * MD3_XYZ_SCALE );
-}
-
-static inline vec3 md3_normal( const md3_vertex_t& v )
-{
-	float pi  = glm::pi<float>();
-	float lat = (((v.normal >> 8) & 255) * (2 * pi)) / 255.0f;
-	float lng = ((v.normal & 255) * (2 * pi)) / 255.0f;
-	return vec3( 
-		glm::cos( lat ) * glm::sin( lng ), 
-//		glm::sin( lat ) * glm::sin( lng ), 
-//		glm::cos( lng )
-		glm::cos( lng ),
-		glm::sin( lat ) * glm::sin( lng )
-	);
-}
-
+static vec3 s_normal_cache[256*256];
+static bool s_normal_ready = false;
 
 md3_loader::md3_loader()
 	: m_md3( nullptr ), m_size( 0 )
 {
-	
+	if ( !s_normal_ready )
+	{
+		float pi      = glm::pi<float>();
+		float convert = (2 * pi) / 255.0f;
+		int n = 0;
+		for ( int lat = 0; lat < 256; ++lat )
+		{
+			float flat    = lat * convert;
+			float sin_lat = glm::sin( flat );
+			float cos_lat = glm::cos( flat );
+			for ( int lng = 0; lng < 256; ++lng, ++n )
+			{
+				float flng    = lng * convert;
+				float sin_lng = glm::sin( flng );
+				float cos_lng = glm::cos( flng );
+				s_normal_cache[n].x = cos_lat * sin_lng;
+//				s_normal_cache[n].y = sin_lat * sin_lng;
+//				s_normal_cache[n].z = cos_lng;
+				s_normal_cache[n].z = sin_lat * sin_lng;
+				s_normal_cache[n].y = cos_lng;
+			}
+		}
+
+		s_normal_ready = true;
+	}
 }
 
@@ -318,4 +328,32 @@
 }
 
+void nv::md3_loader::load_tags( std::vector<mat4>& t, const std::string& tag )
+{
+	md3_t* md3 = (md3_t*)m_md3;
+	t.clear();
+	for ( sint32 f = 0; f < md3->header.num_frames; ++f )
+	{
+		for ( sint32 i = 0; i < md3->header.num_tags; ++i )
+		{
+			const md3_tag_t& rtag = md3->tags[i + md3->header.num_tags * f];
+			std::string rname((char*)(rtag.name));
+			if (rname == tag)
+			{
+				vec4 axisx     = vec4( md3_vec3( rtag.axis[0] ), 0.0 );
+				vec4 axisz     = vec4( md3_vec3( rtag.axis[1] ), 0.0 );
+				vec4 axisy     = vec4( md3_vec3( rtag.axis[2] ), 0.0 );
+				vec4 origin    = vec4( md3_vec3( rtag.origin ),  1.0 );
+				t.emplace_back( axisx, axisy, axisz, origin );
+			}
+		}
+
+	}
+}
+
+sint32 nv::md3_loader::get_max_frames() const
+{
+	return ((md3_t*)m_md3)->header.num_frames;
+}
+
 mat4 md3_loader::get_tag( sint32 frame, const std::string& name ) const
 {
@@ -352,4 +390,6 @@
 	sint32 frame_count   = ( frame == -1 ? md3->header.num_frames : 1 );
 
+	p.reserve( md3->vertices_per_frame * frame_count );
+
 	while ( frame_count > 0 )
 	{
@@ -359,8 +399,9 @@
 			sint32         vcount  = surface.header.num_verts;
 			sint32         offset  = vcount * current_frame;
-			p.reserve( p.size() + vcount );
-			for (sint32 j = 0; j < vcount; ++j )
+			sint32         limit   = vcount + offset;
+			for (sint32 j = offset; j < limit; ++j )
 			{
-				p.push_back( md3_vec3( surface.vertices[j + offset] ) );
+				md3_vertex_t& v = surface.vertices[j];
+				p.push_back( vec3( v.x * MD3_XYZ_SCALE, v.z * MD3_XYZ_SCALE, v.y * MD3_XYZ_SCALE ) );
 			}
 		}
@@ -378,4 +419,6 @@
 	sint32 frame_count   = ( frame == -1 ? md3->header.num_frames : 1 );
 
+	n.reserve( md3->vertices_per_frame * frame_count );
+
 	while ( frame_count > 0 )
 	{
@@ -385,8 +428,8 @@
 			sint32         vcount  = surface.header.num_verts;
 			sint32         offset  = vcount * current_frame;
-			n.reserve( n.size() + vcount );
-			for (sint32 j = 0; j < vcount; ++j )
+			sint32         limit   = vcount + offset;
+			for (sint32 j = offset; j < limit; ++j )
 			{
-				n.push_back( md3_normal( surface.vertices[j + offset] ) );
+				n.push_back( s_normal_cache[ surface.vertices[j].normal ] );
 			}
 		}
Index: trunk/src/time.cc
===================================================================
--- trunk/src/time.cc	(revision 148)
+++ trunk/src/time.cc	(revision 149)
@@ -67,7 +67,7 @@
 }
 
-nv::uint32 nv::get_cpu_us()
+nv::uint64 nv::get_cpu_us()
 {
-	return (uint32)( (f32)( clock() - zero_timer.clock_zero ) / ( (f32)CLOCKS_PER_SEC / 1000000.0 ) ) ;
+	return (uint64)( (f32)( clock() - zero_timer.clock_zero ) / ( (f32)CLOCKS_PER_SEC / 1000000.0 ) ) ;
 }
 
@@ -78,5 +78,5 @@
 	QueryPerformanceCounter(&now);
 	LONGLONG result = now.QuadPart - zero_timer.query_zero.QuadPart;
-	return (uint32) (1000.0 * result / zero_timer.frequency.QuadPart);
+	return (uint32) (1000.0 * result / (double) zero_timer.frequency.QuadPart);
 #else
 	struct timeval now;
@@ -86,5 +86,5 @@
 }
 
-nv::uint32 nv::get_system_us()
+nv::uint64 nv::get_system_us()
 {
 #if NV_COMPILER == NV_MSVC
@@ -92,5 +92,5 @@
 	QueryPerformanceCounter(&now);
 	LONGLONG result = now.QuadPart - zero_timer.query_zero.QuadPart;
-	return (uint32) (1000000.0 * result / zero_timer.frequency.QuadPart);
+	return (uint64) (1000000.0 * result / (double) zero_timer.frequency.QuadPart);
 #else
 	struct timeval now;
