Index: trunk/nv/formats/nmd_loader.hh
===================================================================
--- trunk/nv/formats/nmd_loader.hh	(revision 291)
+++ trunk/nv/formats/nmd_loader.hh	(revision 292)
@@ -91,4 +91,7 @@
 	};
 
+	// HACK : TEMPORARY - will go to it's own file, probably nmd_io
+	void nmd_dump( const nv::mesh_data_pack* model, stream& stream_out );
+
 }
 
Index: trunk/nv/gl/gl_device.hh
===================================================================
--- trunk/nv/gl/gl_device.hh	(revision 291)
+++ trunk/nv/gl/gl_device.hh	(revision 292)
@@ -28,5 +28,5 @@
 		virtual vertex_array* create_vertex_array();
 		virtual image_data* create_image_data( const std::string& filename ); // temporary
-		virtual texture2d* create_texture2d( ivec2 size, image_format aformat, datatype adatatype, sampler asampler, void* data = nullptr );
+		virtual texture2d* create_texture2d( ivec2 size, pixel_format aformat, datatype adatatype, sampler asampler, void* data = nullptr );
 		virtual uint32 get_ticks();
 		virtual void delay( uint32 ms );
Index: trunk/nv/gl/gl_enum.hh
===================================================================
--- trunk/nv/gl/gl_enum.hh	(revision 291)
+++ trunk/nv/gl/gl_enum.hh	(revision 292)
@@ -33,5 +33,5 @@
 	unsigned int stencil_operation_to_enum( stencil_test_face::operation type );
 	unsigned int buffer_hint_to_enum( buffer_hint hint );
-	unsigned int image_format_to_enum( image_format format );
+	unsigned int image_format_to_enum( pixel_format format );
 	unsigned int sampler_filter_to_enum( sampler::filter filter );
 	unsigned int sampler_wrap_to_enum( sampler::wrap wrap );
Index: trunk/nv/gl/gl_texture2d.hh
===================================================================
--- trunk/nv/gl/gl_texture2d.hh	(revision 291)
+++ trunk/nv/gl/gl_texture2d.hh	(revision 292)
@@ -22,5 +22,5 @@
 	{
 	public:
-		gl_texture2d( ivec2 size, image_format aformat, datatype adatatype, sampler asampler, void* data = nullptr );
+		gl_texture2d( ivec2 size, pixel_format aformat, datatype adatatype, sampler asampler, void* data = nullptr );
 		virtual void assign( void* data );
 		virtual void bind( size_t slot = 0 );
Index: trunk/nv/interface/device.hh
===================================================================
--- trunk/nv/interface/device.hh	(revision 291)
+++ trunk/nv/interface/device.hh	(revision 292)
@@ -35,7 +35,16 @@
 		virtual vertex_array* create_vertex_array() = 0;
 		virtual image_data* create_image_data( const std::string& filename ) = 0; // temporary
-		virtual texture2d* create_texture2d( ivec2 size, image_format aformat, datatype adatatype, sampler asampler, void* data = nullptr ) = 0;
+		virtual texture2d* create_texture2d( ivec2 size, pixel_format aformat, datatype adatatype, sampler asampler, void* data = nullptr ) = 0;
 		virtual uint32 get_ticks() = 0;
 		virtual void delay( uint32 ms ) = 0;
+
+		texture2d* create_texture2d( ivec2 size, image_format aformat, sampler asampler, void* data = nullptr )
+		{
+			return create_texture2d( size, aformat.format, aformat.type, asampler, data );
+		}
+		virtual texture2d* create_texture2d( image_data* data, sampler asampler ) 
+		{
+			return create_texture2d( data->get_size(), data->get_format(), asampler, (void*)data->get_data() );
+		}
 
 		template < typename VTX, slot SLOT >
Index: trunk/nv/interface/image_data.hh
===================================================================
--- trunk/nv/interface/image_data.hh	(revision 291)
+++ trunk/nv/interface/image_data.hh	(revision 292)
@@ -20,22 +20,41 @@
 namespace nv
 {
+	enum pixel_format
+	{
+		RGB,
+		RGBA
+	};
+	
+	struct image_format
+	{
+		pixel_format format;
+		datatype     type;
+
+		image_format( pixel_format f = RGB, datatype d = UBYTE ) 
+			: format( f ), type( d )  {}
+	};
+
 	class image_data
 	{
 	public:
-		image_data( ivec2 size, size_t depth, const uint8 * data ) 
-			: m_size( size ), m_depth( depth ), m_data( nullptr )
-		{ 
-			std::size_t bsize = static_cast<std::size_t>(m_size.x * m_size.y) * m_depth;
+		image_data( image_format format, ivec2 size, const uint8 * data ) 
+			: m_format( format ), m_size( size ), m_data( nullptr )	{ initialize( data ); }
+		uint8* release_data() { uint8* r = m_data; m_data = nullptr; return r; }
+		const uint8 * get_data()    const { return m_data; }
+		const ivec2 get_size() const { return m_size; }
+		// TODO : better depth check (template?)
+		size_t get_depth()    const { return m_format.format == RGB ? 3 : 4; }
+		image_format get_format() const { return m_format; }
+		~image_data() {	if (m_data) delete[] m_data; }
+	private:
+		void initialize( const uint8* data )
+		{
+			std::size_t bsize = static_cast<std::size_t>(m_size.x * m_size.y) * get_depth();
 			m_data = new uint8[ bsize ]; 
 			std::copy( data, data + bsize, m_data );
 		}
-		uint8* release_data() { uint8* r = m_data; m_data = nullptr; return r; }
-		const uint8 * get_data()    const { return m_data; }
-		const ivec2 get_size() const { return m_size; }
-		size_t get_depth()    const { return m_depth; }
-		~image_data() {	if (m_data) delete[] m_data; }
-	private:
+
+		image_format m_format;
 		ivec2  m_size;  //!< Defines the size of the image as a vector
-		size_t m_depth; //!< Defines the depth of the image
 		uint8* m_data;  //!< Holder for data
 	};
Index: trunk/nv/interface/texture2d.hh
===================================================================
--- trunk/nv/interface/texture2d.hh	(revision 291)
+++ trunk/nv/interface/texture2d.hh	(revision 292)
@@ -15,4 +15,5 @@
 #include <nv/common.hh>
 #include <nv/math.hh>
+#include <nv/interface/image_data.hh>
 
 namespace nv
@@ -50,16 +51,9 @@
 	};
 
-	enum image_format
-	{
-		RGB,
-		RGBA
-	};
-
 	class texture2d
 	{
 	public:
-
-		texture2d( ivec2 size, image_format aformat, datatype adatatype, sampler asampler ) : 
-			m_size( size ), m_format( aformat ), m_datatype( adatatype ), m_sampler( asampler ) {}
+		texture2d( ivec2 size, pixel_format aformat, datatype adatatype, sampler asampler ) : 
+			m_size( size ), m_format( aformat, adatatype ), m_sampler( asampler ) {}
 		virtual void assign( void* data ) = 0;
 		virtual void bind( size_t slot = 0 ) = 0;
@@ -70,5 +64,6 @@
 		int get_height() const { return m_size.y; }
 		image_format get_format() const { return m_format; }
-		datatype get_datatype() const { return m_datatype; }
+		image_format get_pixel_format() const { return m_format.format; }
+		datatype get_type() const { return m_format.type; }
 		const sampler& get_sampler() const { return m_sampler; }
 		virtual ~texture2d() {}
@@ -76,5 +71,4 @@
 		ivec2        m_size;
 		image_format m_format;
-		datatype     m_datatype;
 		sampler      m_sampler;
 	};
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 );
 }
