Index: /trunk/nv/formats/md3_loader.hh
===================================================================
--- /trunk/nv/formats/md3_loader.hh	(revision 148)
+++ /trunk/nv/formats/md3_loader.hh	(revision 149)
@@ -38,4 +38,6 @@
 		virtual const md3_tag* get_tag( const std::string& name ) const;
 		virtual mat4 get_tag( sint32 frame, const std::string& name ) const;
+		sint32 get_max_frames() const;
+		void load_tags( std::vector<mat4>& t, const std::string& tag );
 		void load_positions( std::vector<vec3>& p, sint32 frame =-1 );
 		void load_normals( std::vector<vec3>& n, sint32 frame =-1 );
Index: /trunk/nv/time.hh
===================================================================
--- /trunk/nv/time.hh	(revision 148)
+++ /trunk/nv/time.hh	(revision 149)
@@ -41,5 +41,5 @@
 	 * Get microsecond count based on std::clock
 	 */
-	uint32 get_cpu_us();
+	uint64 get_cpu_us();
 
 	/**
@@ -51,10 +51,33 @@
 	 * Get microsecond count based on system counter
 	 */
-	uint32 get_system_us();
+	uint64 get_system_us();
 
-	struct cpu_ms_timer { uint32 operator()() { return get_cpu_ms(); } };
-	struct cpu_us_timer { uint32 operator()() { return get_cpu_us(); } };
-	struct system_ms_timer { uint32 operator()() { return get_system_ms(); } };
-	struct system_us_timer { uint32 operator()() { return get_system_us(); } };
+	struct cpu_ms_timer 
+	{ 
+		typedef uint32 value_type;
+		static const value_type second = 1000;    
+		value_type operator()() { return get_cpu_ms(); } 
+	};
+
+	struct cpu_us_timer 
+	{ 
+		typedef uint64 value_type;
+		static const value_type second = 1000000; 
+		uint64 operator()() { return get_cpu_us(); } 
+	};
+
+	struct system_ms_timer 
+	{ 
+		typedef uint32 value_type;
+		static const value_type second = 1000;    
+		value_type operator()() { return get_system_ms(); } 
+	};
+
+	struct system_us_timer 
+	{ 
+		typedef uint64 value_type;
+		static const value_type second = 1000000; 
+		value_type operator()() { return get_system_us(); } 
+	};
 	
 	/**
@@ -65,18 +88,20 @@
 	{
 	public:
+		typedef typename Timer::value_type value_type;
+
 		timer_class()	: last( Timer()() ) {}
 		void mark()	
 		{
-			uint32 now = Timer()();
+			value_type now = Timer()();
 			stored = now - last;
 			last = now;
 		}
-		uint32 elapsed() const
+		value_type elapsed() const
 		{
 			return stored;
 		}
 	private:
-		uint32 last;
-		uint32 stored;
+		value_type last;
+		value_type stored;
 	};
 
@@ -88,12 +113,14 @@
 	{
 	public:
+		typedef typename Timer::value_type value_type;
+
 		fps_counter_class() : frames(1), last(0) {}
 		bool tick()
 		{
-			uint32 now = Timer()();
-			if ( now - last >= 1000 )
+			value_type now = Timer()();
+			if ( now - last >= Timer::second )
 			{
 				value = (static_cast<float>(frames) /
-					static_cast<float>(now - last))*1000;
+					static_cast<float>(now - last))*Timer::second;
 				frames = 1;
 				last = now;
@@ -108,7 +135,7 @@
 		}
 	private:
-		uint32 last;
-		uint32 frames;
-		f32 value;
+		value_type last;
+		uint32     frames;
+		f32        value;
 	};
 
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;
