Index: trunk/src/formats/assimp_loader.cc
===================================================================
--- trunk/src/formats/assimp_loader.cc	(revision 291)
+++ trunk/src/formats/assimp_loader.cc	(revision 292)
@@ -63,8 +63,8 @@
 bool nv::assimp_loader::load( stream& source )
 {
+	load_assimp_library();
 	if ( m_scene != nullptr ) aiReleaseImport( (const aiScene*)m_scene );
 	m_scene = nullptr;
 	m_mesh_count = 0;
-	load_assimp_library();
 	NV_LOG( nv::LOG_NOTICE, "AssImp loading file..." );
 	int size = (int)source.size();
Index: trunk/src/formats/nmd_loader.cc
===================================================================
--- trunk/src/formats/nmd_loader.cc	(revision 291)
+++ trunk/src/formats/nmd_loader.cc	(revision 292)
@@ -157,2 +157,142 @@
 }
 
+// ----------------------------------------------------------------
+// nmd format dump
+// HACK : TEMPORARY - will go to it's own file, probably nmd_io
+static void nmd_dump_mesh( const mesh_data* mesh, stream& stream_out )
+{
+	const std::vector< mesh_raw_channel* >& data  = mesh->get_raw_channels();
+
+	uint32 size = sizeof( nmd_element_header );
+	for ( auto chan : data )
+	{
+		size += sizeof( nmd_element_header ) + sizeof( nmd_stream_header );
+		size += chan->size();
+	}
+
+	nmd_element_header eheader;
+	eheader.type     = nmd_type::MESH;
+	eheader.name     = 0;
+	eheader.children = (uint16)data.size();
+	eheader.size     = size;
+	stream_out.write( &eheader, sizeof( eheader ), 1 );
+
+	for ( auto chan : data )
+	{
+		nmd_element_header cheader;
+		eheader.name     = 0;
+		cheader.type     = nmd_type::STREAM;
+		cheader.children = 0;
+		cheader.size     = chan->size() + sizeof( nmd_stream_header );
+		stream_out.write( &cheader, sizeof( cheader ), 1 );
+
+		nmd_stream_header sheader;
+		sheader.format = chan->desc;
+		sheader.count  = chan->count;
+		stream_out.write( &sheader, sizeof( sheader ), 1 );
+		stream_out.write( chan->data, chan->desc.size, chan->count );
+	}
+}
+
+static void nmd_dump_nodes_data( const mesh_nodes_data* nodes, stream& stream_out, string_table_creator* strings )
+{
+	uint32 total = sizeof( nmd_animation_header );
+	for ( uint32 i = 0; i < nodes->get_count(); ++i )
+	{
+		const mesh_node_data* node = nodes->get_node(i);
+		total += sizeof( nmd_element_header ) + sizeof( nmd_node_header );
+		if ( node->data )
+			for ( uint32 c = 0; c < node->data->get_channel_count(); ++c )
+			{
+				total += sizeof( nmd_element_header ) + sizeof( nmd_key_channel_header );
+				total += node->data->get_channel(c)->size();
+			}
+	}
+
+	nmd_element_header header;
+	header.name     = 0;
+	header.type     = nmd_type::ANIMATION;
+	header.children = (uint16)nodes->get_count();
+	header.size     = total;
+	stream_out.write( &header, sizeof( header ), 1 );
+
+	nmd_animation_header aheader;
+	aheader.frame_rate  = nodes->get_frame_rate();
+	aheader.duration    = nodes->get_duration();
+	aheader.flat        = nodes->is_flat();
+	stream_out.write( &aheader, sizeof( aheader ), 1 );
+
+	for ( uint32 i = 0; i < nodes->get_count(); ++i )
+	{
+		const mesh_node_data* node = nodes->get_node(i);
+		uint32 chan_size  = 0;
+		uint32 chan_count = ( node->data ? node->data->get_channel_count() : 0 );
+		for ( uint32 c = 0; c < chan_count; ++c )
+		{
+			chan_size += sizeof( nmd_element_header ) + sizeof( nv::nmd_key_channel_header );
+			chan_size += node->data->get_channel(c)->size();
+		}
+
+		nmd_element_header eheader;
+		eheader.type     = nmd_type::NODE;
+		eheader.name     = strings->insert( node->name );
+		eheader.children = (uint16)chan_count;
+		eheader.size     = sizeof( nmd_node_header ) + chan_size;
+		stream_out.write( &eheader, sizeof( eheader ), 1 );
+
+		nmd_node_header nheader;
+		nheader.parent_id = (uint16)node->parent_id;
+		nheader.transform = node->transform;
+		stream_out.write( &nheader, sizeof( nheader ), 1 );
+
+		for ( uint32 c = 0; c < chan_count; ++c )
+		{
+			const key_raw_channel* channel = node->data->get_channel(c);
+
+			eheader.type     = nmd_type::KEY_CHANNEL;
+			eheader.children = 0;
+			eheader.size     = sizeof( nmd_key_channel_header ) + channel->size();
+			stream_out.write( &eheader, sizeof( eheader ), 1 );
+
+			nmd_key_channel_header cheader;
+			cheader.format    = channel->desc;
+			cheader.count     = channel->count;
+			stream_out.write( &cheader, sizeof( cheader ), 1 );
+			stream_out.write( channel->data, channel->desc.size, channel->count );
+		}
+	}
+}
+
+void nv::nmd_dump( const mesh_data_pack* model, stream& stream_out )
+{
+	string_table_creator strings;
+	{
+		nmd_header header;
+		header.id       = four_cc<'n','m','f','1'>::value;
+		header.elements = 1; // +1 string array
+		header.elements += model->get_count();
+		if ( model->get_nodes() && model->get_nodes()->get_count() > 0 ) 
+			header.elements += 1;//  +1 bone array
+		header.version  = 1;
+		stream_out.write( &header, sizeof( header ), 1 );
+	}
+
+	for ( uint32 i = 0; i < model->get_count(); ++i )
+	{
+		const mesh_data* mesh = model->get_mesh(i);
+		nmd_dump_mesh( mesh, stream_out );
+	}
+
+	if ( model->get_nodes() && model->get_nodes()->get_count() > 0 )
+	{
+		nmd_dump_nodes_data( model->get_nodes(), stream_out, &strings );
+	}
+
+	nmd_element_header sheader;
+	sheader.type     = nv::nmd_type::STRING_TABLE;
+	sheader.name     = 0;
+	sheader.size     = strings.dump_size();
+	sheader.children = 0;
+	stream_out.write( &sheader, sizeof( sheader ), 1 );
+	strings.dump( &stream_out );
+}
Index: trunk/src/gl/gl_device.cc
===================================================================
--- trunk/src/gl/gl_device.cc	(revision 291)
+++ trunk/src/gl/gl_device.cc	(revision 292)
@@ -81,9 +81,12 @@
 		return nullptr;
 	}
-	image_data* data = new image_data( glm::ivec2( image->w, image->h ), image->format->BytesPerPixel, (nv::uint8*)image->pixels );
+	// TODO: BGR vs RGB, single channel
+	assert( image->format->BytesPerPixel > 2 );
+	image_format format(image->format->BytesPerPixel == 3 ? RGB : RGBA, UBYTE );
+	image_data* data = new image_data( format, glm::ivec2( image->w, image->h ), (nv::uint8*)image->pixels );
 	return data;
 }
 
-texture2d* gl_device::create_texture2d( ivec2 size, image_format aformat, datatype adatatype, sampler asampler, void* data /*= nullptr */ )
+texture2d* gl_device::create_texture2d( ivec2 size, pixel_format aformat, datatype adatatype, sampler asampler, void* data /*= nullptr */ )
 {
 	return new gl_texture2d( size, aformat, adatatype, asampler, data );
Index: trunk/src/gl/gl_enum.cc
===================================================================
--- trunk/src/gl/gl_enum.cc	(revision 291)
+++ trunk/src/gl/gl_enum.cc	(revision 292)
@@ -147,5 +147,5 @@
 }
 
-unsigned int nv::image_format_to_enum( image_format format )
+unsigned int nv::image_format_to_enum( pixel_format format )
 {
 	switch( format )
Index: trunk/src/gl/gl_texture2d.cc
===================================================================
--- trunk/src/gl/gl_texture2d.cc	(revision 291)
+++ trunk/src/gl/gl_texture2d.cc	(revision 292)
@@ -10,5 +10,5 @@
 using namespace nv;
 
-nv::gl_texture2d::gl_texture2d( ivec2 size, image_format aformat, datatype adatatype, sampler asampler, void* data /*= nullptr */ )
+nv::gl_texture2d::gl_texture2d( ivec2 size, pixel_format aformat, datatype adatatype, sampler asampler, void* data /*= nullptr */ )
 	: texture2d( size, aformat, adatatype, asampler ), m_name()
 {
@@ -38,5 +38,5 @@
 {
 	glBindTexture( GL_TEXTURE_2D, m_name.get_value() );
-	glTexImage2D( GL_TEXTURE_2D, 0, (GLint)nv::image_format_to_enum(m_format), m_size.x, m_size.y, 0, nv::image_format_to_enum(m_format), nv::datatype_to_gl_enum(m_datatype), data );
+	glTexImage2D( GL_TEXTURE_2D, 0, (GLint)nv::image_format_to_enum(m_format.format), m_size.x, m_size.y, 0, nv::image_format_to_enum(m_format.format), nv::datatype_to_gl_enum(m_format.type), data );
 	glBindTexture( GL_TEXTURE_2D, 0 );
 }
