Index: /trunk/nv/formats/assimp_loader.hh
===================================================================
--- /trunk/nv/formats/assimp_loader.hh	(revision 286)
+++ /trunk/nv/formats/assimp_loader.hh	(revision 287)
@@ -56,7 +56,8 @@
 	struct assimp_animation
 	{
-		float fps;
-		float duration;
-		bool  pretransformed;
+		uint16 frame_rate;
+		float  duration;
+		bool   flat;
+		uint32 max_frames;
 		dynamic_array< assimp_animated_node_data > nodes;
 	};
@@ -71,9 +72,15 @@
 		virtual size_t get_mesh_count() const { return m_mesh_count; }
 		virtual ~assimp_loader();
+		// TODO: remove
 		assimp_model* release_merged_model();
+		// TODO: implement release_mesh_nodes_data
+		virtual mesh_data_pack* release_mesh_data_pack();
 		assimp_animation* release_animation( size_t index, bool pre_transform );
+		// TODO: implement
+		virtual mesh_nodes_data* release_mesh_nodes_data();
 		bool load_bones( size_t index, std::vector< assimp_bone >& bones );
 		void scene_report() const;
 	private:
+		void load_mesh_data( mesh_data* data, size_t index );
 		void initialize( const string& a_ext, const mat3& a_rotate_transform, float a_scale, uint32 a_assimp_flags );
 		uint32 load_node( assimp_animation* data, const void* vnode, sint32 this_id, sint32 parent_id );
Index: /trunk/nv/formats/md2_loader.hh
===================================================================
--- /trunk/nv/formats/md2_loader.hh	(revision 286)
+++ /trunk/nv/formats/md2_loader.hh	(revision 287)
@@ -29,7 +29,8 @@
 		virtual bool load( stream& source );
 		virtual mesh_data* release_mesh_data( size_t index = 0 );
+		virtual mesh_data_pack* release_mesh_data_pack();
 		virtual size_t get_mesh_count() const { return 1; }
 	private:
-		virtual mesh_data* release_mesh_frame( sint32 frame );
+		void release_mesh_frame( mesh_data* data, sint32 frame );
 		size_t get_max_frames() const;
 		void reindex();
Index: /trunk/nv/formats/md3_loader.hh
===================================================================
--- /trunk/nv/formats/md3_loader.hh	(revision 286)
+++ /trunk/nv/formats/md3_loader.hh	(revision 287)
@@ -36,11 +36,10 @@
 		virtual bool load( stream& source );
 		virtual mesh_data* release_mesh_data( size_t index = 0 );
-		virtual mesh_data* release_mesh_frame( sint32 frame );
+		virtual mesh_data_pack* release_mesh_data_pack();
 		virtual size_t get_mesh_count() const { return 1; }
 		size_t get_max_frames() const;
-		virtual tag_map* create_tag_map();
-		virtual mesh_node_data* release_mesh_node_data( size_t index );
-		virtual size_t get_node_count() const;
+		virtual mesh_nodes_data* release_mesh_nodes_data();
 	private:
+		void release_mesh_frame( mesh_data* data, sint32 frame );
 		key_raw_channel* load_tags( const std::string& tag );
 		void* m_md3;
Index: /trunk/nv/formats/md5_loader.hh
===================================================================
--- /trunk/nv/formats/md5_loader.hh	(revision 286)
+++ /trunk/nv/formats/md5_loader.hh	(revision 287)
@@ -33,5 +33,5 @@
 	};
 
-	struct md5_vtx_data
+	struct md5_vtx_pntiw
 	{
 		vec3  position;
@@ -88,38 +88,4 @@
 	};
 
-	class md5_mesh_instance 
-	{
-		friend class md5_mesh_data;
-	public:
-		const void* data() const { return m_pntdata.data(); }
-		uint32 get_index_count() const { return m_indices; }
-		size_t size() const { return m_pntdata.raw_size(); }
-		void apply( const transform* skeleton );
-		const md5_mesh_data* get_mesh_data() const { return m_data; }
-	private:
-		md5_mesh_instance( const md5_mesh_data* a_data );
-
-		uint32                       m_indices;
-		dynamic_array< md5_vtx_pnt > m_pntdata;
-		dynamic_array< transform >   m_pos_offset;
-		const md5_mesh_data*         m_data;
-	};
-
-	class md5_mesh_data : public mesh_data
-	{
-	public:
-		friend class md5_loader;
-		friend class md5_mesh_instance;
-		md5_mesh_instance* spawn() const;
-	private:
-		uint32*                       m_idata;
-		md5_vtx_t*                    m_tdata;
-		md5_vtx_pnt*                  m_pntdata;
-		std::string                   m_shader;
-		dynamic_array< transform >    m_bone_offset;
-		dynamic_array< md5_vtx_data > m_vtx_data;
-	};
-
-
 	class md5_loader : public mesh_loader
 	{
@@ -129,4 +95,7 @@
 		virtual bool load( stream& source );
 		virtual mesh_data* release_mesh_data( size_t index = 0 );
+		virtual mesh_nodes_data* release_mesh_nodes_data();
+		// TODO: broken due to larger mesh_data structure!
+		virtual mesh_data_pack* release_mesh_data_pack();
 		virtual size_t get_mesh_count() const { return m_meshes.size(); }
 	protected:
@@ -151,5 +120,5 @@
 		};
 	protected:
-		bool prepare_mesh( md5_mesh_data* mdata, md5_weight* weights, md5_weight_info* weight_info );
+		bool prepare_mesh( uint32 vtx_count, mesh_data* mdata, md5_weight* weights, md5_weight_info* weight_info );
 	protected:
 		uint32 m_md5_version;
@@ -157,6 +126,6 @@
 		uint32 m_num_meshes;
 
-		dynamic_array<md5_joint>      m_joints;
-		dynamic_array<md5_mesh_data*> m_meshes;
+		dynamic_array<md5_joint>  m_joints;
+		dynamic_array<mesh_data*> m_meshes;
 	};
 
Index: /trunk/nv/formats/nmd_loader.hh
===================================================================
--- /trunk/nv/formats/nmd_loader.hh	(revision 286)
+++ /trunk/nv/formats/nmd_loader.hh	(revision 287)
@@ -20,9 +20,8 @@
 		MESH,
 		STREAM,
-		BONE_ARRAY,
+		NODE,
 		STRING_TABLE,
 		ANIMATION,
-		ANIMATION_NODE,
-		ANIMATION_CHANNEL,
+		KEY_CHANNEL,
 	};
 
@@ -44,10 +43,10 @@
 	struct nmd_animation_header
 	{
-		float fps;
-		float duration;
-		bool  flat;
+		uint16 frame_rate;
+		float  duration;
+		bool   flat;
 	};
 
-	struct nmd_animation_node_header
+	struct nmd_node_header
 	{
 		sint16 parent_id;
@@ -55,5 +54,5 @@
 	};
 
-	struct nmd_animation_channel_header
+	struct nmd_key_channel_header
 	{
 		key_descriptor format;
@@ -67,60 +66,12 @@
 	};
 
-	struct nmd_bone
-	{
-		uint16 name;
-		mat4   offset;
-	};
-
-	struct nmd_node
-	{
-		uint16 name;
-		sint16 parent_id;
-		mat4   transform;
-		key_data* data;
-	};
-
-	struct nmd_animation
-	{
-		float     fps;
-		float     duration;
-		bool      flat;
-		uint16    node_count;
-		nmd_node* nodes;
-
-		nmd_animation() : node_count(0), nodes( nullptr ) {}
-		~nmd_animation()
-		{
-			for ( uint16 n = 0; n < node_count; ++n )
-			{
-				if ( nodes[n].data ) delete nodes[n].data;
-			}
-			delete[] nodes;
-		}
-	};
-
-	struct nmd_bone_data
-	{
-		nmd_bone* bones;
-		uint16    count;
-
-		nmd_bone_data() : count(0), bones( nullptr ) {}
-		~nmd_bone_data()
-		{
-			if ( bones ) delete[] bones;
-		}
-	};
-
-
-
 	class nmd_loader : public mesh_loader
 	{
 	public:
-		nmd_loader() : m_animation( nullptr ), m_bone_data( nullptr ), m_strings( nullptr ) {}
+		nmd_loader() : m_node_data( nullptr ), m_node_array( nullptr ), m_strings( nullptr ) {}
 		virtual bool load( stream& source );
 		virtual mesh_data* release_mesh_data( size_t index = 0 );
-		virtual nmd_animation* release_animation();
-		virtual nmd_bone_data* release_bone_data();
-		virtual string_table* release_string_table();
+		virtual mesh_nodes_data* release_mesh_nodes_data();
+		virtual mesh_data_pack* release_mesh_data_pack();
 		virtual size_t get_mesh_count() const { return m_meshes.size(); }
 		virtual ~nmd_loader();
@@ -128,52 +79,28 @@
 		void reset();
 		bool load_mesh( stream& source, const nmd_element_header& e );
-		bool load_bones( stream& source, const nmd_element_header& e ); 
 		bool load_strings( stream& source ); 
 		bool load_animation( stream& source, const nmd_element_header& e );
 
-		nmd_animation*            m_animation;
-		nmd_bone_data*            m_bone_data;
+		mesh_nodes_data*          m_node_data;
+		mesh_node_data*           m_node_array;
 		string_table*             m_strings;
+		std::vector< uint16 >     m_mesh_names;
+		std::vector< uint16 >     m_node_names;
 		std::vector< mesh_data* > m_meshes;
 	};
 
 	// TODO: Temporary, find a better way and remove!
-
-	class nmd_temp_model;
-
-	class nmd_temp_animation
+	class nmd_temp_model_data
 	{
+		friend class nmd_temp_animation_data;
 	public:
-		nmd_temp_animation( nmd_loader* loader );
-		void prepare( const nmd_temp_model* model );
-		void animate( mat4* data, uint32 time );
-		virtual uint32 get_frame_rate() const { return uint32(m_animation->fps); }
-		virtual uint32 get_frame_count() const { return uint32(m_animation->duration) / uint32(m_animation->fps); }
-		~nmd_temp_animation();
+		nmd_temp_model_data( nmd_loader* loader );
+		const mesh_data* get_data( uint32 index ) const { return m_mesh_data[index]; }
+		uint32 get_count() const { return m_mesh_data.size(); }
+		const mesh_nodes_data* get_node_data() const { return m_node_data; }
+		~nmd_temp_model_data();
 	private:
-		void animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat );
-
-		std::vector< key_data* > m_data;
-		std::vector< sint16 > m_bone_ids;
-		std::vector< std::vector< uint32 > > m_children;
-		const mat4*    m_offsets;
-		string_table*  m_strings;
-		nmd_animation* m_animation;
-	};
-
-	// TODO: Temporary, find a better way and remove!
-	class nmd_temp_model
-	{
-		friend class nmd_temp_animation;
-	public:
-		nmd_temp_model( nmd_loader* loader );
-		const mesh_data* get_data( uint32 index ) const { return m_mesh_data[index]; }
-		uint32 get_bone_count() const { return m_bone_offsets.size(); }
-		uint32 get_count() const { return m_mesh_data.size(); }
-		~nmd_temp_model();
-	private:
-		std::unordered_map< std::string, nv::uint16 > m_bone_names;
-		std::vector< mat4 >      m_bone_offsets;
 		std::vector< mesh_data*> m_mesh_data;
+		mesh_nodes_data*         m_node_data;
 	};
 
Index: /trunk/nv/formats/obj_loader.hh
===================================================================
--- /trunk/nv/formats/obj_loader.hh	(revision 286)
+++ /trunk/nv/formats/obj_loader.hh	(revision 287)
@@ -26,5 +26,6 @@
 		obj_loader( bool normals = true, bool tangents = false );
 		virtual bool load( stream& source );
-		mesh_data* release_mesh_data( size_t index = 0 );
+		virtual mesh_data* release_mesh_data( size_t index = 0 );
+		virtual mesh_data_pack* release_mesh_data_pack();
 		virtual size_t get_mesh_count() const { return m_meshes.size(); }
 		~obj_loader();
Index: /trunk/nv/gfx/animation.hh
===================================================================
--- /trunk/nv/gfx/animation.hh	(revision 286)
+++ /trunk/nv/gfx/animation.hh	(revision 287)
@@ -73,8 +73,10 @@
 
 			uint32 slot = 0;
-			int index    = -1;
+			int index0  = -1;
+			int index1  = -1;
 			float factor = 1.0f;
 			if ( desc.slots[0].vslot == animation_slot::TIME )
 			{
+				NV_ASSERT( desc.slots[0].offset == 0, "time offset not zero!" );
 				slot++;
 				keyfresult--;
@@ -84,12 +86,12 @@
 					return keyfresult;
 				}
-				uint32 toffset = desc.slots[0].offset / 4;
-				for ( int i = 0 ; i < (int)count - 1 ; i++ )
+				for ( int i = 1 ; i < (int)count ; i++ )
 				{
-					if ( time < fdata[ i * keyfsize + keyfsize + toffset ] ) { index = i; break; }
+					if ( time < fdata[ i * keyfsize ] ) { index0 = i - 1; break; }
 				}
-				NV_ASSERT( index >= 0, "animation time fail!");
-				float time0  = fdata[ index * keyfsize + toffset ];
-				float time1  = fdata[ index * keyfsize + keyfsize + toffset ];
+				NV_ASSERT( index0 >= 0, "animation time fail!");
+				index1 = index0 + 1;
+				float time0  = fdata[ index0 * keyfsize ];
+				float time1  = fdata[ index1 * keyfsize ];
 				float delta  = time1 - time0;
 				factor = glm::clamp( (time - time0) / delta, 0.0f, 1.0f );
@@ -102,6 +104,7 @@
 					return keyfresult;
 				}
-				index  = glm::clamp<int>( int( time ), 0, count - 2 );
-				factor = glm::clamp<float> ( time - index, 0.0f, 1.0f );
+				index0 = glm::clamp<int>( int( time ), 0, count - 2 );
+				index1 = index0 + 1;
+				factor = glm::clamp<float> ( time - index0, 0.0f, 1.0f );
 			}
 			uint32 ret = 0;
@@ -110,6 +113,6 @@
 				ret += nv::interpolate_raw( 
 					desc.slots[slot], factor, 
-					fdata + index * keyfsize + desc.slots[slot].offset / 4,
-					fdata + index * keyfsize + keyfsize + desc.slots[slot].offset / 4,
+					fdata + index0 * keyfsize + desc.slots[slot].offset / 4,
+					fdata + index1 * keyfsize + desc.slots[slot].offset / 4,
 					result + ret );
 			}
Index: /trunk/nv/gfx/keyframed_mesh.hh
===================================================================
--- /trunk/nv/gfx/keyframed_mesh.hh	(revision 286)
+++ /trunk/nv/gfx/keyframed_mesh.hh	(revision 287)
@@ -24,5 +24,5 @@
 			: animation_entry( name ), m_start( a_start ), m_frames( a_frames ), m_fps( a_fps ), m_looping( a_loop ) {}
 		virtual uint32 get_frame_rate() const { return m_fps; }
-		virtual uint32 get_frame_count() const { return m_frames; }
+		virtual float get_duration() const { return (float)m_frames; }
 		virtual bool is_looping() const { return m_looping; }
 	protected:
@@ -37,9 +37,10 @@
 	{
 	public:
-		keyframed_mesh( device* a_device, mesh_data* a_data, tag_map* a_tag_map );
+		keyframed_mesh( device* a_device, const mesh_data* a_data, const mesh_nodes_data* a_tag_map );
 		virtual size_t get_index_count() const { return m_index_count; }
 		virtual void run_animation( animation_entry* a_anim );
 		size_t get_max_frames() const;
-		transform get_tag( uint32 tag_id ) const;
+		virtual transform get_node_transform( uint32 node_id ) const;
+		virtual mat4 get_node_matrix( uint32 node_id ) const;
 		virtual void setup_animation( uint32 start, uint32 count, uint32 fps, bool loop );
 		virtual void set_frame( uint32 frame );
@@ -58,6 +59,6 @@
 		};
 
-		mesh_data* m_mesh_data;
-		tag_map*   m_tag_map;
+		const mesh_data*       m_mesh_data;
+		const mesh_nodes_data* m_tag_map;
 
 		uint32 m_start_frame;
@@ -79,5 +80,5 @@
 	{
 	public:
-		keyframed_mesh_gpu( device* a_device, mesh_data* a_data, tag_map* a_tag_map, program* a_program );
+		keyframed_mesh_gpu( device* a_device, const mesh_data* a_data, const mesh_nodes_data* a_tag_map, program* a_program );
 		void update( uint32 ms );
 	private:
@@ -92,5 +93,5 @@
 	{
 	public:
-		keyframed_mesh_cpu( device* a_device, mesh_data* a_data, tag_map* a_tag_map );
+		keyframed_mesh_cpu( device* a_device, const mesh_data* a_data, const mesh_nodes_data* a_tag_map );
 		void update( uint32 ms );
 	private:
Index: /trunk/nv/gfx/skeletal_mesh.hh
===================================================================
--- /trunk/nv/gfx/skeletal_mesh.hh	(revision 286)
+++ /trunk/nv/gfx/skeletal_mesh.hh	(revision 287)
@@ -37,12 +37,18 @@
 	{
 	public:
-		skeletal_mesh( device* a_device, md5_mesh_data* a_mesh_data );
-		virtual size_t get_index_count() const { return m_mesh_data->get_index_count(); }
+		skeletal_mesh( device* a_device, const mesh_data* a_mesh_data, const mesh_nodes_data* bones );
+		virtual size_t get_index_count() const { return m_indices; }
 		virtual void run_animation( animation_entry* a_anim );
 		virtual void update_animation( animation_entry* a_anim, uint32 a_anim_time );
 		virtual ~skeletal_mesh();
 	protected:
-		md5_mesh_instance* m_mesh_data;
-		std::vector< transform > m_transform;
+		uint32                       m_indices;
+		dynamic_array< md5_vtx_pnt > m_pntdata;
+		dynamic_array< transform >   m_pos_offset;
+		dynamic_array< transform >   m_bone_offset;
+;
+		const mesh_data*             m_data;
+		const md5_vtx_pntiw*         m_vtx_data;
+		dynamic_array< transform >   m_transform;
 	};
 
@@ -50,15 +56,19 @@
  	{
  	public:
- 		skeletal_animation_entry_gpu( const std::string& name, nmd_temp_animation* anim, bool a_looping ) 
- 			: animation_entry( name ), m_animation( anim ), m_looping( a_looping ), m_prepared( false ) {}
- 		virtual uint32 get_frame_rate() const { return 0; }
- 		virtual uint32 get_frame_count() const { return 0; }
+ 		skeletal_animation_entry_gpu( const std::string& name, const mesh_nodes_data* anim, bool a_looping );
+ 		virtual uint32 get_frame_rate() const { return m_node_data->get_frame_rate(); }
+ 		virtual float get_duration() const { return m_node_data->get_duration(); }
  		virtual bool is_looping() const { return m_looping; }
-		void prepare( const nmd_temp_model* m_model );
-		void update_skeleton( mat4* tr, uint32 time );
+		void prepare( const mesh_nodes_data* bones );
+		void update_skeleton( mat4* tr, uint32 time ) const;
+		~skeletal_animation_entry_gpu();
  	protected:
- 		nmd_temp_animation* m_animation;
-		bool           m_prepared;
- 		bool           m_looping;
+		void animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat ) const;
+		const mesh_nodes_data* m_node_data;
+		std::vector< uint32 >* m_children;
+		sint16* m_bone_ids;
+		mat4* m_offsets;
+		bool m_prepared;
+ 		bool m_looping;
  	};
 
@@ -66,5 +76,5 @@
 	{
 	public:
-		skeletal_mesh_gpu( device* a_device, const nmd_temp_model* a_model, uint32 index, bool primary );
+		skeletal_mesh_gpu( device* a_device, const mesh_data* a_mesh, const mesh_nodes_data* a_bone_data );
 		virtual size_t get_index_count() const { return m_index_count; }
 		virtual void run_animation( animation_entry* a_anim );
@@ -72,9 +82,11 @@
 		virtual void update_animation( animation_entry* a_anim, uint32 
 			a_anim_time );
+		virtual transform get_node_transform( uint32 node_id ) const;
+		virtual mat4 get_node_matrix( uint32 node_id ) const;
+		~skeletal_mesh_gpu() { delete m_transform; }
 	protected:
-		const nmd_temp_model* m_model;
-		bool m_primary;
+		const mesh_nodes_data* m_bone_data;
 		uint32 m_index_count;
-		std::vector< mat4 > m_transform;
+		mat4*  m_transform;
 	};
 
Index: /trunk/nv/handle.hh
===================================================================
--- /trunk/nv/handle.hh	(revision 286)
+++ /trunk/nv/handle.hh	(revision 287)
@@ -244,4 +244,11 @@
 		}
 
+		const value_type* get( handle h ) const
+		{
+			if ( h.is_nil() ) return nullptr;
+			sint32 eindex = m_indexes.get_index( h );
+			return eindex >= 0 ? &(m_data[ eindex ]) : nullptr;
+		}
+
 		value_type* get( handle h )
 		{
Index: /trunk/nv/interface/animated_mesh.hh
===================================================================
--- /trunk/nv/interface/animated_mesh.hh	(revision 286)
+++ /trunk/nv/interface/animated_mesh.hh	(revision 287)
@@ -28,5 +28,5 @@
 		virtual const std::string& get_name() const { return m_name; }
 		virtual uint32 get_frame_rate() const = 0;
-		virtual uint32 get_frame_count() const = 0;
+		virtual float get_duration() const = 0;
 		virtual bool is_looping() const = 0;
 		virtual ~animation_entry() {}
@@ -41,6 +41,6 @@
 		virtual void update_animation( animation_entry*, uint32 ) {}
 		virtual void run_animation( animation_entry* ) {}
-		virtual transform get_tag( uint32 ) const { return transform(); }
-
+		virtual transform get_node_transform( uint32 ) const { return transform(); }
+		virtual mat4 get_node_matrix( uint32 ) const { return mat4(); }
 	};
 
Index: /trunk/nv/interface/device.hh
===================================================================
--- /trunk/nv/interface/device.hh	(revision 286)
+++ /trunk/nv/interface/device.hh	(revision 287)
@@ -104,13 +104,16 @@
 			{
 				const mesh_raw_channel* channel = channels[ch];
-				if ( channel->is_index() )
+				if ( channel->count > 0 )
 				{
-					index_buffer* ib = create_index_buffer( hint, channel->size(), channel->data );
-					va->set_index_buffer( ib, channel->desc.slots[0].etype, true );
-				}
-				else
-				{
-					vertex_buffer* vb = create_vertex_buffer( hint, channel->size(), channel->data );
-					va->add_vertex_buffers( vb, channel );
+					if ( channel->is_index() )
+					{
+						index_buffer* ib = create_index_buffer( hint, channel->size(), channel->data );
+						va->set_index_buffer( ib, channel->desc.slots[0].etype, true );
+					}
+					else
+					{
+						vertex_buffer* vb = create_vertex_buffer( hint, channel->size(), channel->data );
+						va->add_vertex_buffers( vb, channel );
+					}
 				}
 			}
Index: /trunk/nv/interface/mesh_data.hh
===================================================================
--- /trunk/nv/interface/mesh_data.hh	(revision 286)
+++ /trunk/nv/interface/mesh_data.hh	(revision 287)
@@ -79,5 +79,7 @@
 	{
 	public:
-		mesh_data() : m_index_channel( nullptr ) {}
+		explicit mesh_data() : m_index_channel( nullptr ) {}
+		explicit mesh_data( const std::string& name ) : m_name(name), m_index_channel( nullptr ) {}
+
 		void add_channel( mesh_raw_channel* channel ) 
 		{
@@ -137,4 +139,19 @@
 		}
 
+		void set_name( const std::string& name ) { m_name = name; }
+		const std::string& get_name() const { return m_name; }
+
+		void move_to( mesh_data& other )
+		{
+			other.m_name          = m_name;
+			other.m_index_channel = m_index_channel;
+			for ( auto c : m_channels )
+			{
+				other.m_channels.push_back( c );
+			}
+			m_channels.clear();
+			m_index_channel = nullptr;
+		}
+
 		virtual ~mesh_data()
 		{
@@ -142,9 +159,9 @@
 		}
 	private:
+		std::string                      m_name;
 		std::vector< mesh_raw_channel* > m_channels;
 		mesh_raw_channel*                m_index_channel;
 	};
 
-
 }
 
Index: /trunk/nv/interface/mesh_loader.hh
===================================================================
--- /trunk/nv/interface/mesh_loader.hh	(revision 286)
+++ /trunk/nv/interface/mesh_loader.hh	(revision 287)
@@ -26,35 +26,4 @@
 {
 
-	// TODO: change to generic nodes!
-	class tag_map
-	{
-	public:
-		typedef std::unordered_map< std::string, uint32 > map;
-
-		tag_map() {}
-
-		const key_data* get_tag( uint32 id ) const
-		{
-			return m_data[id];
-		}
-		uint32 get_tag_id( const std::string& key ) const
-		{
-			return m_map.at( key );
-		}
-		void insert( const std::string& key, key_data* chan )
-		{
-			m_map[ key ] = m_data.size();
-			m_data.push_back( chan );
-		}
-		~tag_map()
-		{
-			for ( auto t : m_data )
-				delete t;
-		}
-	private:
-		std::vector< key_data* > m_data;
-		map m_map;
-	};
-
 	struct mesh_node_data
 	{
@@ -66,4 +35,83 @@
 	};
 
+	class mesh_nodes_data
+	{
+	public:
+		explicit mesh_nodes_data( const string& name, uint32 count, mesh_node_data* nodes )
+			: m_name( name ), m_count( count ), m_nodes( nodes ), m_frame_rate(0), m_duration(0.0f), m_flat( false )
+		{
+		}
+
+		explicit mesh_nodes_data( const string& name, uint32 count, mesh_node_data* nodes,
+			uint16 a_fps, float a_frames, bool a_flat )
+			: m_name( name ), m_count( count ), m_nodes( nodes ), m_frame_rate(a_fps), m_duration(a_frames), m_flat( a_flat )
+		{
+		}
+
+		size_t get_count() const { return m_count; }
+
+		const mesh_node_data* get_node( size_t i ) const 
+		{
+			if ( i >= m_count ) return nullptr;
+			return &m_nodes[i];
+		}
+
+		key_data* release_node_data( size_t i )
+		{
+			key_data* result = m_nodes[i].data;
+			m_nodes[i].data = nullptr;
+			return result;
+		}
+
+		bool is_flat() const { return m_flat; }
+		uint16 get_frame_rate() const { return m_frame_rate; }
+		float get_duration() const { return m_duration; }
+		void set_name( const std::string& name ) { m_name = name; }
+		const string& get_name() const { return m_name; }
+
+		~mesh_nodes_data()
+		{
+			if ( m_count > 0 )
+				for ( uint32 i = 0; i < m_count; ++i ) delete m_nodes[i].data;
+			delete[] m_nodes;
+		}
+
+	private:
+		string m_name;
+		uint32 m_count;
+		mesh_node_data* m_nodes;
+		uint16  m_frame_rate;
+		float   m_duration;
+		bool    m_flat;
+	};
+
+	class mesh_data_pack
+	{
+	public:
+		explicit mesh_data_pack( uint32 a_count, mesh_data* a_meshes, mesh_nodes_data* a_nodes = nullptr )
+		{
+			m_count  = a_count;
+			m_meshes = a_meshes;
+			m_nodes  = a_nodes;
+		}
+		const mesh_data* get_mesh( uint32 index ) const
+		{
+			if ( index >= m_count ) return nullptr;
+			return &m_meshes[ index ];
+		}
+		uint32 get_count() const { return m_count; }
+		const mesh_nodes_data* get_nodes() const { return m_nodes; }
+		~mesh_data_pack()
+		{
+			delete[] m_meshes;
+			delete   m_nodes;
+		}
+	private:
+		uint32           m_count;
+		mesh_data*       m_meshes;
+		mesh_nodes_data* m_nodes;
+	};
+
+
 	class mesh_loader
 	{
@@ -71,10 +119,9 @@
 		mesh_loader() {}
 		virtual ~mesh_loader() {}
-		virtual tag_map* create_tag_map() { return nullptr; }
 		virtual bool load( stream& source ) = 0;
 		virtual mesh_data* release_mesh_data( size_t index = 0 ) = 0;
+		virtual mesh_data_pack* release_mesh_data_pack() = 0;
 		virtual size_t get_mesh_count() const = 0;
-		virtual mesh_node_data* release_mesh_node_data( size_t ) { return nullptr; }
-		virtual size_t get_node_count() const { return 0; }
+		virtual mesh_nodes_data* release_mesh_nodes_data() { return nullptr; }
 	};
 
Index: /trunk/nv/interface/program.hh
===================================================================
--- /trunk/nv/interface/program.hh	(revision 286)
+++ /trunk/nv/interface/program.hh	(revision 287)
@@ -140,5 +140,5 @@
 			// }
 			// TODO: nicer check
-			NV_ASSERT( count <= base->get_length(), "LENGTH CHECK FAIL" );
+			NV_ASSERT( (int)count <= base->get_length(), "LENGTH CHECK FAIL" );
 			((uniform<T>*)( base ))->set_value( value, count );
 		}
Index: /trunk/src/formats/assimp_loader.cc
===================================================================
--- /trunk/src/formats/assimp_loader.cc	(revision 286)
+++ /trunk/src/formats/assimp_loader.cc	(revision 287)
@@ -79,7 +79,13 @@
 {
 	if ( index >= m_mesh_count ) return nullptr;
-	mesh_data* result = new mesh_data();
+	mesh_data* result = new mesh_data;
+	load_mesh_data( result, index );
+	return result;
+}
+void nv::assimp_loader::load_mesh_data( mesh_data* data, size_t index )
+{
 	const aiScene* scene = (const aiScene*)m_scene;
 	const aiMesh*  mesh  = scene->mMeshes[ index ];
+	data->set_name( mesh->mName.data );
 
 	vec3 vertex_offset     = glm::vec3(); 
@@ -94,5 +100,5 @@
 		channel = mesh_raw_channel::create< assimp_plain_vtx >( mesh->mNumVertices );
 
-	result->add_channel( channel );
+	data->add_channel( channel );
 	for (unsigned int i=0; i<mesh->mNumVertices; i++)
 	{
@@ -139,5 +145,5 @@
 
 	mesh_raw_channel* ichannel = mesh_raw_channel::create_index( USHORT, mesh->mNumFaces * 3 );
-	result->add_channel( ichannel );
+	data->add_channel( ichannel );
 	uint16* indices = (uint16*)ichannel->data;
 	for (unsigned int i=0; i<mesh->mNumFaces; i++)
@@ -149,6 +155,4 @@
 		}
 	}
-
-	return result;
 }
 
@@ -322,9 +326,18 @@
 	const aiAnimation* anim = scene->mAnimations[0]; // if implemented, change in load_node also
 
-	result->fps            = (float)anim->mTicksPerSecond;
+	result->frame_rate     = (uint16)anim->mTicksPerSecond;
 	result->duration       = (float)anim->mDuration;
-	result->pretransformed = pre_transform;
+	result->flat           = pre_transform;
+	result->max_frames     = 0;
 
 	load_node( result, root, 0, -1 );
+
+	// DAE pre_transform hack
+	if ( result->flat && result->frame_rate == 1 )
+	{
+		result->frame_rate = 32;
+		result->duration   = (float)result->max_frames;
+	}
+
 	return result;
 }
@@ -371,5 +384,5 @@
 	if (anode) 
 	{
-		if ( data->pretransformed )
+		if ( data->flat )
 		{
 			create_transformed_keys( &a_data, anode, parent_id >= 0 ? &(data->nodes[ parent_id ]) : nullptr );
@@ -379,4 +392,5 @@
 			create_direct_keys( &a_data, anode );
 		}
+		data->max_frames = glm::max<uint32>( a_data.data->get_channel(0)->count, data->max_frames );
 	}
 
@@ -386,4 +400,5 @@
 		next = load_node( data, node->mChildren[i], next, this_id );
 	}
+
 	return next;
 }
@@ -432,11 +447,11 @@
 	key_raw_channel* raw_pchannel = key_raw_channel::create<assimp_key_p>( node->mNumPositionKeys );
 	key_raw_channel* raw_rchannel = key_raw_channel::create<assimp_key_r>( node->mNumRotationKeys );
-	key_raw_channel* raw_schannel = key_raw_channel::create<assimp_key_s>( node->mNumScalingKeys );
+	//key_raw_channel* raw_schannel = key_raw_channel::create<assimp_key_s>( node->mNumScalingKeys );
 	data->data->add_channel( raw_pchannel );
 	data->data->add_channel( raw_rchannel );
-	data->data->add_channel( raw_schannel );
+	//data->data->add_channel( raw_schannel );
 	assimp_key_p* pchannel = ((assimp_key_p*)(raw_pchannel->data));
 	assimp_key_r* rchannel = ((assimp_key_r*)(raw_rchannel->data));
-	assimp_key_s* schannel = ((assimp_key_s*)(raw_schannel->data));
+	//assimp_key_s* schannel = ((assimp_key_s*)(raw_schannel->data));
 
 	for ( unsigned np = 0; np < node->mNumPositionKeys; ++np )
@@ -450,24 +465,40 @@
 		rchannel[np].rotation = glm::quat_cast( m_r33 * glm::mat3_cast( assimp_quat_cast(node->mRotationKeys[np].mValue ) ) * m_ri33 );
 	}
-	if ( node->mNumScalingKeys > 0 )
-	{
-		nv::vec3 scale_vec0 = assimp_vec3_cast( node->mScalingKeys[0].mValue );
-		float scale_value   = glm::length( glm::abs( scale_vec0 - nv::vec3(1,1,1) ) );
-		if ( node->mNumScalingKeys > 1 || scale_value > 0.001 ) 
-		{
-			NV_LOG( nv::LOG_WARNING, "scale key significant!" );
-			for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
-			{
-				schannel[np].time  = (float)node->mScalingKeys[np].mTime;
-				schannel[np].scale = assimp_vec3_cast(node->mScalingKeys[np].mValue);
-			}
-		}
-		else 
-		{
-			schannel[0].time  = (float)node->mScalingKeys[0].mTime;
-			schannel[0].scale = assimp_vec3_cast(node->mScalingKeys[0].mValue);
-		}
-	}
-
-}
-
+// 	if ( node->mNumScalingKeys > 0 )
+// 	{
+// 		nv::vec3 scale_vec0 = assimp_vec3_cast( node->mScalingKeys[0].mValue );
+// 		float scale_value   = glm::length( glm::abs( scale_vec0 - nv::vec3(1,1,1) ) );
+// 		if ( node->mNumScalingKeys > 1 || scale_value > 0.001 ) 
+// 		{
+// 			NV_LOG( nv::LOG_WARNING, "scale key significant!" );
+// 			for ( unsigned np = 0; np < node->mNumRotationKeys; ++np )
+// 			{
+// 				schannel[np].time  = (float)node->mScalingKeys[np].mTime;
+// 				schannel[np].scale = assimp_vec3_cast(node->mScalingKeys[np].mValue);
+// 			}
+// 		}
+// 		else 
+// 		{
+// 			schannel[0].time  = (float)node->mScalingKeys[0].mTime;
+// 			schannel[0].scale = assimp_vec3_cast(node->mScalingKeys[0].mValue);
+// 		}
+// 	}
+
+}
+
+mesh_data_pack* nv::assimp_loader::release_mesh_data_pack()
+{
+	mesh_data* meshes = new mesh_data[ m_mesh_count ];
+	for ( uint32 i = 0; i < m_mesh_count; ++i )
+	{
+		load_mesh_data(&meshes[i],i);
+	}
+	return new mesh_data_pack( m_mesh_count, meshes, release_mesh_nodes_data() );
+}
+
+mesh_nodes_data* nv::assimp_loader::release_mesh_nodes_data()
+{
+	// TODO: implement
+	return nullptr;
+}
+
Index: /trunk/src/formats/md2_loader.cc
===================================================================
--- /trunk/src/formats/md2_loader.cc	(revision 286)
+++ /trunk/src/formats/md2_loader.cc	(revision 287)
@@ -315,8 +315,10 @@
 mesh_data* nv::md2_loader::release_mesh_data( size_t )
 {
-	return release_mesh_frame( -1 );
-}
-
-mesh_data* nv::md2_loader::release_mesh_frame( sint32 frame )
+	mesh_data* data = new mesh_data( "md2_mesh" );
+	release_mesh_frame( data, -1 );
+	return data;
+}
+
+void nv::md2_loader::release_mesh_frame( mesh_data* data, sint32 frame )
 {
 	md2_t* md2 = (md2_t*)m_md2;
@@ -366,8 +368,13 @@
 	}
 
-	mesh_data* result = new mesh_data();
-	result->add_channel( mc_pn );
-	result->add_channel( mc_t );
-	result->add_channel( ic );
-	return result;
-}
+	data->add_channel( mc_pn );
+	data->add_channel( mc_t );
+	data->add_channel( ic );
+}
+
+mesh_data_pack* nv::md2_loader::release_mesh_data_pack()
+{
+	mesh_data* data = new mesh_data[1];
+	release_mesh_frame( &data[0], -1 );
+	return new mesh_data_pack( 1, data );
+}
Index: /trunk/src/formats/md3_loader.cc
===================================================================
--- /trunk/src/formats/md3_loader.cc	(revision 286)
+++ /trunk/src/formats/md3_loader.cc	(revision 287)
@@ -321,8 +321,10 @@
 mesh_data* nv::md3_loader::release_mesh_data( size_t )
 {
-	return release_mesh_frame( -1 );
-}
-
-mesh_data* nv::md3_loader::release_mesh_frame( sint32 frame )
+	mesh_data* data = new mesh_data;
+	release_mesh_frame( data, -1 );
+	return data;
+}
+
+void nv::md3_loader::release_mesh_frame( mesh_data* data, sint32 frame )
 {
 	md3_t* md3 = (md3_t*)m_md3;
@@ -394,48 +396,38 @@
 	}
 
-	mesh_data* result = new mesh_data();
-	result->add_channel( mc_pn );
-	result->add_channel( mc_t );
-	result->add_channel( ic );
-	return result;
-}
-
-tag_map* nv::md3_loader::create_tag_map()
+	data->set_name( (char*)md3->header.name );
+	data->add_channel( mc_pn );
+	data->add_channel( mc_t );
+	data->add_channel( ic );
+}
+
+mesh_nodes_data* nv::md3_loader::release_mesh_nodes_data()
 {
 	md3_t* md3 = (md3_t*)m_md3;
-	tag_map* result = new tag_map();
-	for ( sint32 i = 0; i < md3->header.num_tags; ++i )
+	uint32 node_count = md3->header.num_tags;
+	if ( node_count == 0 ) return nullptr;;
+	mesh_node_data* nodes = new mesh_node_data[ node_count ];
+	for ( uint32 i = 0; i < node_count; ++i )
 	{
 		const md3_tag_t& rtag = md3->tags[i];
 		std::string name( (char*)(rtag.name) );
 
-		key_data* data = new key_data;
-		data->add_channel( load_tags( name ) );
-		result->insert( name, data );
-	}
-	return result;
-}
-
-mesh_node_data* nv::md3_loader::release_mesh_node_data( size_t index )
-{
-	md3_t* md3 = (md3_t*)m_md3;
-	const md3_tag_t& rtag = md3->tags[index];
-	std::string name( (char*)(rtag.name) );
-
-	mesh_node_data* result = new mesh_node_data;
-	result->transform = mat4();
-	result->name      = name;
-	result->parent_id = -1;
-	result->target_id = -1;
-	result->data = new key_data;
+		nodes[i].transform = mat4();
+		nodes[i].name      = name;
+		nodes[i].parent_id = -1;
+		nodes[i].target_id = -1;
+		nodes[i].data = new key_data;
 	
-	key_raw_channel* keys = load_tags( name );
-	result->data->add_channel( keys );
-	return result;
-}
-
-size_t nv::md3_loader::get_node_count() const
-{
-	return ((md3_t*)m_md3)->header.num_tags;
+		key_raw_channel* keys = load_tags( name );
+		nodes[i].data->add_channel( keys );
+	}
+	return new mesh_nodes_data( "tags", node_count, nodes );
+}
+
+mesh_data_pack* nv::md3_loader::release_mesh_data_pack()
+{
+	mesh_data* data = new mesh_data[1];
+	release_mesh_frame( &data[0], -1 );
+	return new mesh_data_pack( 1, data, release_mesh_nodes_data() );
 }
 
Index: /trunk/src/formats/md5_loader.cc
===================================================================
--- /trunk/src/formats/md5_loader.cc	(revision 286)
+++ /trunk/src/formats/md5_loader.cc	(revision 287)
@@ -10,5 +10,4 @@
 #include "nv/logging.hh"
 #include "nv/io/std_stream.hh"
-#include "nv/profiler.hh"
 #include <cstring>
 
@@ -45,5 +44,4 @@
 bool md5_loader::load( stream& source )
 {
-	NV_PROFILE( "Load MD5" ); // 16XXms original
 	std_stream sstream( &source );
 	std::string command;
@@ -96,7 +94,9 @@
 		else if ( command == "mesh" )
 		{
-			md5_mesh_data* mesh = new md5_mesh_data();
-
-			int num_verts, num_tris, num_weights;
+			mesh_data* mesh = new mesh_data("md5_mesh");
+
+			int num_verts   = 0;
+			int num_tris    = 0;
+			int num_weights = 0;
 
 			discard( sstream, "{" );
@@ -106,7 +106,7 @@
 				if ( command == "shader" )
 				{
-					sstream >> mesh->m_shader;
-					remove_quotes( mesh->m_shader );
-					// texturePath.replace_extension( ".tga" );
+					std::string shader;
+					sstream >> shader;
+					remove_quotes( shader );
 					next_line( sstream );
 				}
@@ -115,13 +115,16 @@
 					sstream >> num_verts; 
 
+					md5_vtx_t* tdata = nullptr;
 					{
 						mesh_raw_channel* ch_pnt = mesh_raw_channel::create<md5_vtx_pnt>( num_verts );
 						mesh_raw_channel* ch_t   = mesh_raw_channel::create<md5_vtx_t>( num_verts );
-						mesh->m_pntdata          = (md5_vtx_pnt*)ch_pnt->data;
-						mesh->m_tdata            = (md5_vtx_t*)ch_t->data;
+						mesh_raw_channel* ch_pntiw = mesh_raw_channel::create<md5_vtx_pntiw>( num_verts );
+						tdata = (md5_vtx_t*)ch_t->data;
 						mesh->add_channel( ch_pnt );
 						mesh->add_channel( ch_t );
+						// TODO: hack to prevent rendering
+						ch_pntiw->count = 0;
+						mesh->add_channel( ch_pntiw );
 					}
-					mesh->m_vtx_data.resize( num_verts );
 					weight_info.resize( num_verts );
 
@@ -138,5 +141,5 @@
 						weight_info[i].start_weight = start_weight;
 						weight_info[i].weight_count = weight_count;
-						mesh->m_tdata[i].texcoord = texcoord;
+						tdata[i].texcoord = texcoord;
 					}  
 				}
@@ -147,5 +150,4 @@
 					mesh_raw_channel* ch_i = mesh_raw_channel::create_index<uint32>( num_tris * 3 );
 					uint32* vtx_i                = (uint32*)ch_i->data;
-					mesh->m_idata                = vtx_i;
 					uint32 idx = 0;
 					mesh->add_channel( ch_i );
@@ -190,5 +192,5 @@
 			}
 
-			prepare_mesh( mesh, weights.data(), weight_info.data() );
+			prepare_mesh( weight_info.size(), mesh, weights.data(), weight_info.data() );
 
 			m_meshes[ m_num_meshes ] = mesh;
@@ -201,15 +203,8 @@
 }
 
-bool md5_loader::prepare_mesh( md5_mesh_data* mdata, md5_weight* weights, md5_weight_info* weight_info )
-{
-	uint32 vtx_count = mdata->m_vtx_data.size();
-	md5_vtx_pnt* vtcs = mdata->m_pntdata;
-
-	mdata->m_bone_offset.resize( m_joints.size() );
-	for ( uint32 i = 0; i < m_joints.size(); ++i )
-	{
-		transform j( m_joints[i].pos, m_joints[i].orient );
-		mdata->m_bone_offset[i] = j.inverse();
-	}
+bool md5_loader::prepare_mesh( uint32 vtx_count, mesh_data* mdata, md5_weight* weights, md5_weight_info* weight_info )
+{
+	md5_vtx_pnt* vtcs = (md5_vtx_pnt*)mdata->get_channel< md5_vtx_pnt >()->data; 
+	md5_vtx_pntiw* vtx_data = (md5_vtx_pntiw*)mdata->get_channel< md5_vtx_pntiw >()->data; 
 
 	for ( uint32 i = 0; i < vtx_count; ++i )
@@ -217,5 +212,5 @@
 		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_pntiw& vdata = vtx_data[i];
 		md5_vtx_pnt& vtc = vtcs[i];
 
@@ -268,11 +263,14 @@
 	}
 
+	const uint32*    idata = (uint32*)mdata->get_index_channel()->data;
+	const md5_vtx_t* tdata = mdata->get_channel_data<md5_vtx_t>();
+
 	// Prepare normals
 	uint32 tri_count = mdata->get_count() / 3;
 	for ( unsigned int i = 0; i < tri_count; ++i )
 	{
-		uint32 ti0 = mdata->m_idata[ i * 3 ];
-		uint32 ti1 = mdata->m_idata[ i * 3 + 1 ];
-		uint32 ti2 = mdata->m_idata[ i * 3 + 2 ];
+		uint32 ti0 = idata[ i * 3 ];
+		uint32 ti1 = idata[ i * 3 + 1 ];
+		uint32 ti2 = idata[ i * 3 + 2 ];
  
 		glm::vec3 v1 = vtcs[ ti0 ].position;
@@ -288,7 +286,7 @@
 		vtcs[ ti2 ].normal += normal;
 
-		const vec2& w1 = mdata->m_tdata[ ti0 ].texcoord;
-		const vec2& w2 = mdata->m_tdata[ ti1 ].texcoord;
-		const vec2& w3 = mdata->m_tdata[ ti2 ].texcoord;
+		const vec2& w1 = tdata[ ti0 ].texcoord;
+		const vec2& w2 = tdata[ ti1 ].texcoord;
+		const vec2& w3 = tdata[ ti2 ].texcoord;
 
 		vec2 st1 = w3 - w1;
@@ -306,5 +304,5 @@
 	for ( size_t i = 0; i < vtx_count; ++i )
 	{
-		md5_vtx_data& vdata = mdata->m_vtx_data[i];
+		md5_vtx_pntiw& vdata = vtx_data[i];
 
 		glm::vec3 normal  = glm::normalize( vtcs[i].normal );
@@ -541,8 +539,32 @@
 }
 
-md5_mesh_instance* nv::md5_mesh_data::spawn() const
-{
-	return new md5_mesh_instance( this );
-}
+mesh_nodes_data* nv::md5_loader::release_mesh_nodes_data()
+{
+	mesh_node_data* nodes = new mesh_node_data[ m_num_joints ];
+	for ( uint32 i = 0; i < m_num_joints; ++i )
+	{
+		mesh_node_data& node = nodes[i];
+		node.name      = m_joints[i].name;
+		node.target_id = -1;
+		node.parent_id = -1;
+		node.transform = transform( m_joints[i].pos, m_joints[i].orient ).inverse().extract();
+		node.data      = nullptr;
+	}
+	return new mesh_nodes_data( "nodes", m_num_joints, nodes );
+}
+
+mesh_data_pack* nv::md5_loader::release_mesh_data_pack()
+{
+	uint32 size = m_meshes.size();
+	mesh_data* meshes = new mesh_data[ size ];
+	for ( uint32 i = 0; i < size; ++i )
+	{
+		m_meshes[i]->move_to( meshes[i] );
+		delete m_meshes[i];
+		m_meshes[i] = nullptr;
+	}
+	return new mesh_data_pack( size, meshes, release_mesh_nodes_data() );
+}
+
 
 nv::md5_loader::~md5_loader()
@@ -551,38 +573,2 @@
 }
 
-nv::md5_mesh_instance::md5_mesh_instance( const md5_mesh_data* a_data ) 
-	: m_data( a_data ), m_indices( 0 )
-{
-	m_indices = m_data->get_count();
-	m_pntdata.assign( m_data->m_pntdata, m_data->m_vtx_data.size() );
-}
-
-void nv::md5_mesh_instance::apply( const transform* skeleton )
-{
-	NV_PROFILE("md5::apply");
-	size_t skeleton_size = m_data->m_bone_offset.size();
-	size_t vertex_count  = m_pntdata.size();
-	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];
-	}
-
-	std::fill( m_pntdata.raw_data(), m_pntdata.raw_data() + m_pntdata.raw_size(), 0 );
-	for ( unsigned int i = 0; i < vertex_count; ++i )
-	{
-		const md5_vtx_data& vert = m_data->m_vtx_data[i];
-		md5_vtx_pnt& result = m_pntdata[i];
-
-		for ( size_t j = 0; j < 4; ++j )
-		{
-			int   index  = vert.boneindex[j];
-			float weight = vert.boneweight[j];
-			const quat& orient      = skeleton[index].get_orientation();
-			const transform& offset = m_pos_offset[index];
-			result.position += offset.transformed( vert.position )        * weight;
-			result.normal   += ( orient * vert.normal  ) * weight;
-			result.tangent  += ( orient * vert.tangent ) * weight;
-		}
-	}
-}
Index: /trunk/src/formats/nmd_loader.cc
===================================================================
--- /trunk/src/formats/nmd_loader.cc	(revision 286)
+++ /trunk/src/formats/nmd_loader.cc	(revision 287)
@@ -7,4 +7,5 @@
 #include "nv/formats/nmd_loader.hh"
 #include "nv/io/std_stream.hh"
+#include "nv/string.hh"
 
 using namespace nv;
@@ -24,5 +25,4 @@
 		case nmd_type::MESH           : load_mesh( source, element_header ); break;
 		case nmd_type::ANIMATION      : load_animation( source, element_header ); break;
-		case nmd_type::BONE_ARRAY     : load_bones( source, element_header ); break;
 		case nmd_type::STRING_TABLE   : load_strings( source ); break;
 		default: NV_ASSERT( false, "UNKNOWN NMD ELEMENT!" ); break;
@@ -47,4 +47,5 @@
 		mesh->add_channel( channel );
 	}
+	m_mesh_names.push_back( e.name );
 	m_meshes.push_back( mesh );
 	return true;
@@ -54,6 +55,20 @@
 {
 	mesh_data* result = m_meshes[ index ];
+	if ( m_strings ) result->set_name( m_strings->get( m_mesh_names[ index ] ) );
 	m_meshes[ index ] = nullptr;
 	return result;
+}
+
+mesh_data_pack* nv::nmd_loader::release_mesh_data_pack()
+{
+	uint32 size = m_meshes.size();
+	mesh_data* meshes = new mesh_data[ size ];
+	for ( uint32 i = 0; i < size; ++i )
+	{
+		m_meshes[i]->move_to( meshes[i] );
+		delete m_meshes[i];
+	}
+	m_meshes.clear();
+	return new mesh_data_pack( size, meshes, release_mesh_nodes_data() );
 }
 
@@ -61,11 +76,13 @@
 {
 	for ( auto mesh : m_meshes ) if ( mesh ) delete mesh;
-	if ( m_animation ) delete m_animation;
 	if ( m_strings )   delete m_strings;
-	if ( m_bone_data ) delete m_bone_data;
+	if ( m_node_data ) delete m_node_data;
 	m_meshes.clear();
-	m_animation = nullptr;
-	m_bone_data = nullptr;
-	m_strings   = nullptr;
+	m_mesh_names.clear();
+	m_node_names.clear();
+
+	m_node_data  = nullptr;
+	m_node_array = nullptr;
+	m_strings    = nullptr;
 }
 
@@ -82,70 +99,32 @@
 }
 
-bool nv::nmd_loader::load_bones( stream& source, const nmd_element_header& e )
-{
-	NV_ASSERT( m_bone_data == nullptr, "MULTIPLE BONE ENTRIES!" );
-	m_bone_data = new nmd_bone_data;
-	m_bone_data->bones = new nmd_bone[ e.children ];
-	m_bone_data->count = (uint16)e.children;
-	source.read( m_bone_data->bones, sizeof( nmd_bone ), e.children );
-	return true;
-}
-
-nmd_animation* nv::nmd_loader::release_animation()
-{
-	nmd_animation* result = m_animation;
-	m_animation = nullptr;
-	return result;
-}
-
-nmd_bone_data* nv::nmd_loader::release_bone_data()
-{
-	nmd_bone_data* result = m_bone_data;
-	m_bone_data = nullptr;
-	return result;
-}
-
-string_table* nv::nmd_loader::release_string_table()
-{
-	string_table* result = m_strings;
-	m_strings = nullptr;
-	return result;
-}
-
-
 bool nv::nmd_loader::load_animation( stream& source, const nmd_element_header& e )
 {
-	NV_ASSERT( m_animation == nullptr, "MULTIPLE ANIMATION ENTRIES!" );
-	nmd_animation_header header;
-	source.read( &header, sizeof( header ), 1 );
-	m_animation = new nmd_animation;
-	m_animation->fps        = header.fps;
-	m_animation->duration   = header.duration;
-	m_animation->flat       = header.flat;
-	m_animation->node_count = (uint16)e.children;
-	m_animation->nodes      = new nmd_node[ e.children ];
+	NV_ASSERT( m_node_data == nullptr, "MULTIPLE NODE ENTRIES!" );
+	nmd_animation_header animation_header;
+	source.read( &animation_header, sizeof( animation_header ), 1 );
+	m_node_array = new mesh_node_data[ e.children ];
 	for ( uint32 i = 0; i < e.children; ++i )
 	{
 		nmd_element_header element_header;
 		source.read( &element_header, sizeof( element_header ), 1 );
-		NV_ASSERT( element_header.type == nmd_type::ANIMATION_NODE, "ANIMATION_NODE expected!" );
-		m_animation->nodes[i].name          = element_header.name;
-
+		NV_ASSERT( element_header.type == nmd_type::NODE, "NODE expected!" );
+		m_node_names.push_back( element_header.name );
 		uint16 ch_count = element_header.children;
 
-		nmd_animation_node_header node_header;
+		nmd_node_header node_header;
 		source.read( &node_header, sizeof( node_header ), 1 );
-		m_animation->nodes[i].parent_id     = node_header.parent_id;
-		m_animation->nodes[i].transform     = node_header.transform;
-		m_animation->nodes[i].data          = nullptr;
+		m_node_array[i].parent_id     = node_header.parent_id;
+		m_node_array[i].transform     = node_header.transform;
+		m_node_array[i].data          = nullptr;
 		if ( ch_count > 0 )
 		{
 			key_data* kdata = new key_data;
-			m_animation->nodes[i].data = kdata;
+			m_node_array[i].data = kdata;
 			for ( uint32 c = 0; c < ch_count; ++c )
 			{
 				source.read( &element_header, sizeof( element_header ), 1 );
-				NV_ASSERT( element_header.type == nmd_type::ANIMATION_CHANNEL, "ANIMATION_CHANNEL expected!" );
-				nv::nmd_animation_channel_header cheader;
+				NV_ASSERT( element_header.type == nmd_type::KEY_CHANNEL, "CHANNEL expected!" );
+				nv::nmd_key_channel_header cheader;
 				source.read( &cheader, sizeof( cheader ), 1 );
 				key_raw_channel* channel = key_raw_channel::create( cheader.format, cheader.count );
@@ -155,116 +134,30 @@
 		}
 	}
+	m_node_data = new mesh_nodes_data( "animation", e.children, m_node_array, animation_header.frame_rate, animation_header.duration, animation_header.flat );
 	return true;
 }
 
+mesh_nodes_data* nv::nmd_loader::release_mesh_nodes_data()
+{
+	if ( m_node_data )
+	{
+		if ( m_strings )
+		{
+			for ( uint32 i = 0; i < m_node_data->get_count(); ++i )
+			{
+				m_node_array[i].name = m_strings->get( m_node_names[i] );
+			}
+		}
+		mesh_nodes_data* result = m_node_data;
+		m_node_data = nullptr;
+		m_node_array = nullptr;
+		return result;
+	}
+	return nullptr;
+}
 
 // TEMPORARY
-nv::nmd_temp_animation::nmd_temp_animation( nmd_loader* loader )
-{
-	m_animation = loader->release_animation();
-	m_strings   = loader->release_string_table();
 
-	for ( uint32 n = 0; n < m_animation->node_count; ++n )
-	{
-		nmd_node& node = m_animation->nodes[n];
-		m_data.push_back( node.data );
-		node.data = nullptr;
-	}
-
-	if ( !m_animation->flat )
-	{
-		m_children.resize( m_animation->node_count );
-		for ( nv::uint32 n = 0; n < m_animation->node_count; ++n )
-		{
-			const nmd_node& node = m_animation->nodes[n];
-			if ( node.parent_id != -1 )
-			{
-				m_children[ node.parent_id ].push_back( n );
-			}
-		}
-	}
-
-	m_bone_ids.resize( m_animation->node_count );
-}
-
-nv::nmd_temp_animation::~nmd_temp_animation()
-{
-	for ( auto node : m_data ) delete node;
-	delete m_animation;
-	delete m_strings;
-}
-
-void nv::nmd_temp_animation::prepare( const nmd_temp_model* model )
-{
-	m_offsets = model->m_bone_offsets.data();
-	for ( uint32 n = 0; n < m_animation->node_count; ++n )
-	{
-		const nmd_node& node = m_animation->nodes[n];
-		sint16 bone_id = -1;
-
-		auto bi = model->m_bone_names.find( m_strings->get( node.name ) );
-		if ( bi != model->m_bone_names.end() )
-		{
-			bone_id = bi->second;
-		}
-		m_bone_ids[n] = bone_id;
-	}
-}
-
-void nv::nmd_temp_animation::animate( mat4* data, uint32 time )
-{
-	float tick_time = ( time / 1000.0f ) * m_animation->fps;
-	float anim_time = fmodf( tick_time, m_animation->duration );
-
-	if ( !m_animation->flat )
-	{
-		animate_rec( data, anim_time, 0, mat4() );
-		return;
-	}
-
-	for ( uint32 n = 0; n < m_animation->node_count; ++n )
-		if ( m_bone_ids[n] >= 0 )
-		{
-			const nmd_node* node = &m_animation->nodes[ n ];
-			nv::mat4 node_mat( node->transform );
-
-			if ( m_data[n] )
-			{
-				node_mat = m_data[n]->get_matrix( anim_time );
-			}
-
-			sint16 bone_id = m_bone_ids[n];
-			data[ bone_id ] = node_mat * m_offsets[ bone_id ];
-		}
-
-}
-
-void nv::nmd_temp_animation::animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat )
-{
-	// TODO: fix transforms, which are now embedded,
-	//       see note in assimp_loader.cc:load_node
-	const nmd_node* node = &m_animation->nodes[ node_id ];
-	mat4 node_mat( node->transform );
-
-	if ( m_data[ node_id ] )
-	{
-		node_mat = m_data[ node_id ]->get_matrix( time );
-	}
-
-	mat4 global_mat = parent_mat * node_mat;
-
-	sint16 bone_id = m_bone_ids[ node_id ];
-	if ( bone_id >= 0 )
-	{
-		data[ bone_id ] = global_mat * m_offsets[ bone_id ];
-	}
-
-	for ( auto child : m_children[ node_id ] )
-	{
-		animate_rec( data, time, child, global_mat );
-	}
-}
-
-nv::nmd_temp_model::nmd_temp_model( nmd_loader* loader )
+nv::nmd_temp_model_data::nmd_temp_model_data( nmd_loader* loader )
 {
 	for ( unsigned m = 0; m < loader->get_mesh_count(); ++m )
@@ -272,18 +165,8 @@
 		m_mesh_data.push_back(loader->release_mesh_data(m));
 	}
-	nmd_bone_data* bone_data = loader->release_bone_data();
-	string_table*  strings   = loader->release_string_table();
-
-	for ( nv::uint16 bi = 0; bi < bone_data->count; ++bi )
-	{
-		m_bone_names[ strings->get( bone_data->bones[bi].name ) ] = bi;
-		m_bone_offsets.push_back( bone_data->bones[bi].offset );
-	}
-
-	delete bone_data;
-	delete strings;
+	m_node_data = loader->release_mesh_nodes_data();
 }
 
-nv::nmd_temp_model::~nmd_temp_model()
+nv::nmd_temp_model_data::~nmd_temp_model_data()
 {
 	for ( unsigned m = 0; m < m_mesh_data.size(); ++m )
@@ -291,3 +174,4 @@
 		delete m_mesh_data[m];
 	}
+	delete m_node_data;
 }
Index: /trunk/src/formats/obj_loader.cc
===================================================================
--- /trunk/src/formats/obj_loader.cc	(revision 286)
+++ /trunk/src/formats/obj_loader.cc	(revision 287)
@@ -50,4 +50,6 @@
 	std::string line;
 	std::string cmd;
+	std::string name;
+	std::string next_name;
 
 	std::size_t size;
@@ -77,4 +79,5 @@
 bool obj_reader::read_stream( std::istream& stream )
 {
+	name = next_name;
 	bool added_faces = false;
 	f32 x, y, z;
@@ -146,5 +149,8 @@
 		if ( cmd == "g" )
 		{
-			if (added_faces) return true;
+			ss >> next_name;
+			if (added_faces) 
+				return true;
+			name = next_name;
 			continue;
 		}
@@ -335,5 +341,5 @@
 		channel->count = reader->size * 3;
 
-		mesh_data* mesh = new mesh_data();
+		mesh_data* mesh = new mesh_data(reader->name);
 		mesh->add_channel( channel );
 		m_meshes.push_back( mesh );
@@ -357,2 +363,15 @@
 	for ( auto mesh : m_meshes ) if ( mesh ) delete mesh;
 }
+
+mesh_data_pack* nv::obj_loader::release_mesh_data_pack()
+{
+	uint32 size = m_meshes.size();
+	mesh_data* meshes = new mesh_data[ size ];
+	for ( uint32 i = 0; i < size; ++i )
+	{
+		m_meshes[i]->move_to( meshes[i] );
+		delete m_meshes[i];
+	}
+	m_meshes.clear();
+	return new mesh_data_pack( size, meshes );
+}
Index: /trunk/src/gfx/keyframed_mesh.cc
===================================================================
--- /trunk/src/gfx/keyframed_mesh.cc	(revision 286)
+++ /trunk/src/gfx/keyframed_mesh.cc	(revision 287)
@@ -15,5 +15,5 @@
 using namespace nv;
 
-nv::keyframed_mesh::keyframed_mesh( device* a_device, mesh_data* a_data, tag_map* a_tag_map )
+nv::keyframed_mesh::keyframed_mesh( device* a_device, const mesh_data* a_data, const mesh_nodes_data* a_tag_map )
 	: animated_mesh()
 	, m_mesh_data( a_data )
@@ -40,12 +40,18 @@
 }
 
-transform keyframed_mesh::get_tag( uint32 tag ) const
+transform keyframed_mesh::get_node_transform( uint32 node_id ) const
 {
 	NV_ASSERT( m_tag_map, "TAGMAP FAIL" );
-	const key_data* data = m_tag_map->get_tag( tag );
+	NV_ASSERT( node_id < m_tag_map->get_count(), "TAGMAP FAIL" );
+	const key_data* data = m_tag_map->get_node( node_id )->data;
 	NV_ASSERT( data, "TAG FAIL" );
 	transform last = data->get_raw_transform( m_last_frame );
 	transform next = data->get_raw_transform( m_next_frame );
 	return interpolate( last, next, m_interpolation  );
+}
+
+mat4 keyframed_mesh::get_node_matrix( uint32 node_id ) const
+{
+	return get_node_transform( node_id ).extract();
 }
 
@@ -125,5 +131,5 @@
 }
 
-nv::keyframed_mesh_gpu::keyframed_mesh_gpu( device* a_device, mesh_data* a_data, tag_map* a_tag_map, program* a_program )
+nv::keyframed_mesh_gpu::keyframed_mesh_gpu( device* a_device, const mesh_data* a_data, const mesh_nodes_data* a_tag_map, program* a_program )
 	: keyframed_mesh( a_device, a_data, a_tag_map )
 	, m_loc_next_position( 0 )
@@ -159,5 +165,5 @@
 }
 
-nv::keyframed_mesh_cpu::keyframed_mesh_cpu( device* a_device, mesh_data* a_data, tag_map* a_tag_map )
+nv::keyframed_mesh_cpu::keyframed_mesh_cpu( device* a_device, const mesh_data* a_data, const mesh_nodes_data* a_tag_map )
 	: keyframed_mesh( a_device, a_data, a_tag_map )
 {
Index: /trunk/src/gfx/skeletal_mesh.cc
===================================================================
--- /trunk/src/gfx/skeletal_mesh.cc	(revision 286)
+++ /trunk/src/gfx/skeletal_mesh.cc	(revision 287)
@@ -11,9 +11,17 @@
 
 
-nv::skeletal_mesh::skeletal_mesh( device* a_device, md5_mesh_data* a_mesh_data )
+nv::skeletal_mesh::skeletal_mesh( device* a_device, const mesh_data* a_mesh_data, const mesh_nodes_data* bones )
 	: animated_mesh()
-	, m_mesh_data( nullptr )
-{
-	m_mesh_data = a_mesh_data->spawn();
+	, m_data( a_mesh_data )
+{
+	const mesh_raw_channel* pnt_chan = a_mesh_data->get_channel<md5_vtx_pnt>();
+	m_pntdata.assign( (const md5_vtx_pnt*)pnt_chan->data, pnt_chan->count );
+	m_bone_offset.resize( bones->get_count() );
+	for ( uint32 i = 0; i < bones->get_count(); ++i )
+	{
+		m_bone_offset[i] = transform( bones->get_node(i)->transform );
+	}
+	m_vtx_data  = a_mesh_data->get_channel_data<md5_vtx_pntiw>();
+	m_indices   = a_mesh_data->get_count();
 	m_va        = a_device->create_vertex_array( a_mesh_data, nv::STREAM_DRAW );
 }
@@ -28,8 +36,36 @@
 		uint32 new_time = a_anim_time % anim_duration;
 		anim->update_skeleton( m_transform.data(), (float)new_time * 0.001f );
-		m_mesh_data->apply( m_transform.data() );
+
+		//m_mesh_data->apply( m_transform.data() );
+		{
+			size_t skeleton_size = m_bone_offset.size();
+			size_t vertex_count  = m_pntdata.size();
+			m_pos_offset.resize( skeleton_size );
+			for ( unsigned int i = 0; i < skeleton_size; ++i )
+			{
+				m_pos_offset[i] = m_transform[i] * m_bone_offset[i];
+			}
+
+			std::fill( m_pntdata.raw_data(), m_pntdata.raw_data() + m_pntdata.raw_size(), 0 );
+			for ( unsigned int i = 0; i < vertex_count; ++i )
+			{
+				const md5_vtx_pntiw& vert = m_vtx_data[i];
+
+				for ( size_t j = 0; j < 4; ++j )
+				{
+					int   index  = vert.boneindex[j];
+					float weight = vert.boneweight[j];
+					const quat& orient      = m_transform[index].get_orientation();
+					const transform& offset = m_pos_offset[index];
+					m_pntdata[i].position += offset.transformed( vert.position )        * weight;
+					m_pntdata[i].normal   += ( orient * vert.normal  ) * weight;
+					m_pntdata[i].tangent  += ( orient * vert.tangent ) * weight;
+				}
+			}
+		}
+
 		vertex_buffer* vb = m_va->find_buffer( nv::slot::POSITION );
 		vb->bind();
-		vb->update( m_mesh_data->data(), 0, m_mesh_data->size() );
+		vb->update( m_pntdata.data(), 0, m_pntdata.raw_size() );
 		vb->unbind();
 	}
@@ -39,5 +75,4 @@
 {
 	delete m_va;
-	delete m_mesh_data;
 }
 
@@ -52,29 +87,135 @@
 }
 
-void nv::skeletal_animation_entry_gpu::update_skeleton( mat4* data, uint32 time )
-{
-	m_animation->animate( data, time );
-}
-
-void nv::skeletal_animation_entry_gpu::prepare( const nmd_temp_model* m_model )
-{
-	m_animation->prepare( m_model );
-}
-
-nv::skeletal_mesh_gpu::skeletal_mesh_gpu( device* a_device, const nmd_temp_model* a_model, uint32 index, bool primary )
-	: animated_mesh(), m_primary( primary ), m_model( a_model )
-{
-	const mesh_data* data = a_model->get_data( index );
-	m_va          = a_device->create_vertex_array( data, nv::STATIC_DRAW );
-	m_index_count = data->get_count();
+
+nv::skeletal_animation_entry_gpu::skeletal_animation_entry_gpu( const std::string& name, const mesh_nodes_data* anim, bool a_looping ) 
+	: animation_entry( name )
+	, m_node_data( anim )
+{
+	uint32 node_count = m_node_data->get_count();
+
+	m_prepared  = false;
+	m_looping   = a_looping;
+	m_children  = nullptr;
+	m_offsets   = nullptr;
+	m_bone_ids  = new sint16[ node_count ];
+
+	if ( !m_node_data->is_flat() )
+	{
+		m_children = new std::vector< uint32 >[ node_count ];
+		for ( uint32 n = 0; n < node_count; ++n )
+		{
+			const mesh_node_data* node = m_node_data->get_node(n);
+			if ( node->parent_id != -1 )
+			{
+				m_children[ node->parent_id ].push_back( n );
+			}
+		}
+	}
+}
+
+void nv::skeletal_animation_entry_gpu::update_skeleton( mat4* data, uint32 time ) const
+{
+	float tick_time = ( time * 0.001f ) * m_node_data->get_frame_rate();
+	float anim_time = fmodf( tick_time, m_node_data->get_duration() );
+
+	if ( !m_node_data->is_flat() )
+	{
+		animate_rec( data, anim_time, 0, mat4() );
+		return;
+	}
+
+	for ( uint32 n = 0; n < m_node_data->get_count(); ++n )
+		if ( m_bone_ids[n] >= 0 )
+		{
+			const mesh_node_data* node = m_node_data->get_node(n);
+			nv::mat4 node_mat( node->transform );
+
+			if ( node->data )
+			{
+				node_mat = node->data->get_matrix( anim_time );
+			}
+
+			sint16 bone_id = m_bone_ids[n];
+			data[ bone_id ] = node_mat * m_offsets[ bone_id ];
+		}
+}
+
+void nv::skeletal_animation_entry_gpu::prepare( const mesh_nodes_data* bones )
+{
+	if ( m_prepared ) return;
+	std::unordered_map< std::string, nv::uint16 > bone_names;
+	m_offsets = new mat4[ bones->get_count() ];
+	for ( nv::uint16 bi = 0; bi < bones->get_count(); ++bi )
+	{
+		const mesh_node_data* bone = bones->get_node(bi);
+		bone_names[ bone->name ] = bi;
+		m_offsets[bi] = bone->transform;
+	}
+
+	for ( uint32 n = 0; n < m_node_data->get_count(); ++n )
+	{
+		const mesh_node_data* node = m_node_data->get_node(n);
+		sint16 bone_id = -1;
+
+		auto bi = bone_names.find( node->name );
+		if ( bi != bone_names.end() )
+		{
+			bone_id = bi->second;
+		}
+		m_bone_ids[n] = bone_id;
+	}
+	m_prepared = true;
+}
+
+void nv::skeletal_animation_entry_gpu::animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat ) const
+{
+	// TODO: fix transforms, which are now embedded,
+	//       see note in assimp_loader.cc:load_node
+	const mesh_node_data* node = m_node_data->get_node( node_id );
+	mat4 node_mat( node->transform );
+
+	if ( node->data )
+	{
+		node_mat = node->data->get_matrix( time );
+	}
+
+	mat4 global_mat = parent_mat * node_mat;
+
+	sint16 bone_id = m_bone_ids[ node_id ];
+	if ( bone_id >= 0 )
+	{
+		data[ bone_id ] = global_mat * m_offsets[ bone_id ];
+	}
+
+	for ( auto child : m_children[ node_id ] )
+	{
+		animate_rec( data, time, child, global_mat );
+	}
+}
+
+nv::skeletal_animation_entry_gpu::~skeletal_animation_entry_gpu()
+{
+	delete[] m_offsets;
+	delete[] m_children;
+	delete[] m_bone_ids;
+}
+
+nv::skeletal_mesh_gpu::skeletal_mesh_gpu( device* a_device, const mesh_data* a_mesh, const mesh_nodes_data* a_bone_data )
+	: animated_mesh(), m_bone_data( a_bone_data ), m_transform( nullptr )
+{
+	m_va          = a_device->create_vertex_array( a_mesh, nv::STATIC_DRAW );
+	m_index_count = a_mesh->get_count();
+	if ( m_bone_data )
+	{
+		m_transform = new mat4[ m_bone_data->get_count() ];
+	}
 }
 
 void nv::skeletal_mesh_gpu::run_animation( animation_entry* a_anim )
 {
-	if ( m_primary && a_anim != nullptr )
+	if ( m_bone_data && a_anim != nullptr )
 	{
 		skeletal_animation_entry_gpu * anim = (skeletal_animation_entry_gpu*)(a_anim);
-		m_transform.resize( m_model->get_bone_count() );
-		anim->prepare( m_model );
+		anim->prepare( m_bone_data );
 		update_animation( a_anim, 0 );
 	}
@@ -83,8 +224,8 @@
 void nv::skeletal_mesh_gpu::update_animation( animation_entry* a_anim, uint32 a_anim_time )
 {
-	if ( m_primary && a_anim )
+	if ( m_bone_data && a_anim )
 	{
 		skeletal_animation_entry_gpu * anim = (skeletal_animation_entry_gpu*)a_anim;
-		anim->update_skeleton( m_transform.data(), a_anim_time );
+		anim->update_skeleton( m_transform, a_anim_time );
 	}
 }
@@ -92,5 +233,15 @@
 void nv::skeletal_mesh_gpu::update( program* a_program ) const
 {
-	if (m_primary)
-		a_program->set_uniform_array( "nv_m_bones", m_transform );
-}
+	if ( m_bone_data )
+		a_program->set_uniform_array( "nv_m_bones", m_transform, m_bone_data->get_count() );
+}
+
+nv::transform nv::skeletal_mesh_gpu::get_node_transform( uint32 node_id ) const
+{
+	return transform( m_transform[ node_id ] );
+}
+
+nv::mat4 nv::skeletal_mesh_gpu::get_node_matrix( uint32 node_id ) const
+{
+	return m_transform[ node_id ];
+}
