Index: /trunk/nv/ecs/ecs.hh
===================================================================
--- /trunk/nv/ecs/ecs.hh	(revision 498)
+++ /trunk/nv/ecs/ecs.hh	(revision 499)
@@ -77,4 +77,20 @@
 				return dead_eindex;
 			}
+
+			index_type remove_swap( index_type dead_eindex )
+			{
+				if ( size_t( dead_eindex ) >= m_handles.size() ) return -1;
+				handle_type h = m_handles[ dead_eindex ];
+				handle_type swap_handle = m_handles.back();
+				if ( dead_eindex != static_cast<index_type>( m_handles.size() - 1 ) )
+				{
+					m_handles[unsigned( dead_eindex )] = swap_handle;
+					m_indexes[swap_handle.index()] = dead_eindex;
+				}
+				m_handles.pop_back();
+				m_indexes[ h.index() ] = -1;
+				return dead_eindex;
+			}
+
 
 			void clear()
@@ -265,4 +281,15 @@
 			}
 
+			virtual void remove( index_type i )
+			{
+				index_type dead_eindex = m_index.remove_swap( i );
+				if ( dead_eindex == -1 ) return;
+				if ( dead_eindex != static_cast<index_type>( m_data.size() - 1 ) )
+				{
+					m_data[unsigned( dead_eindex )] = move( m_data.back() );
+				}
+				m_data.pop_back();
+			}
+
 			virtual bool handle_message( const message_type& )
 			{
Index: /trunk/nv/engine/particle_engine.hh
===================================================================
--- /trunk/nv/engine/particle_engine.hh	(revision 498)
+++ /trunk/nv/engine/particle_engine.hh	(revision 499)
@@ -137,24 +137,31 @@
 	};
 
+	struct particle_system_group_tag {};
+	typedef handle< uint32, 16, 16, particle_system_group_tag > particle_system_group;
+	struct particle_system_tag {};
+	typedef handle< uint32, 16, 16, particle_system_tag > particle_system;
 
 	struct particle_system_info
 	{
-		bool test;
 		uint32                last_update;
 		particle*             particles;
-		particle_quad*        quads;
 		particle_emmiter_info emmiters[MAX_PARTICLE_EMMITERS];
 
 		uint32                count;
+		vec2                  texcoords[4];
+		particle_system_group group;
+
+		const particle_system_data*    data;
+	};
+
+	struct particle_system_group_info
+	{
+		uint32                count;
+		uint32                quota;
 		vertex_array          vtx_array;
 		buffer                vtx_buffer;
-		//		bool                           own_va;
-		//		uint32                         offset;
-
-		const particle_system_data*    data;
-	};
-
-	struct particle_system_tag {};
-	typedef handle< uint32, 16, 16, particle_system_tag > particle_system;
+		bool                  local;
+		particle_quad*        quads;
+	};
 
 	class particle_engine
@@ -164,8 +171,12 @@
 		void reset();
 		void load( lua::table_guard& table );
-		void draw( particle_system system, const render_state& rs, const scene_state& ss );
-		particle_system create_system( const string_view& id );
+		void draw( particle_system_group group, const render_state& rs, const scene_state& ss );
+		void prepare( particle_system_group group );
+		particle_system_group create_group( uint32 max_particles );
+		particle_system create_system( const string_view& id, particle_system_group group );
+		void release( particle_system_group group );
 		void release( particle_system system );
-		void update( particle_system system, const scene_state& s, uint32 ms );
+		void update( const scene_state& s, uint32 ms );
+		void update( particle_system system );
 		void set_texcoords( particle_system system, vec2 a, vec2 b );
 		void register_emmiter_type( const string_view& name, particle_emmiter_func func );
@@ -193,6 +204,10 @@
 		vec3     m_camera_pos;
 		uint32   m_last_update;
-
-		handle_store< particle_system_info, particle_system > m_systems;
+		vec2     m_tc_a;
+		vec2     m_tc_b;
+
+		handle_store< particle_system_info, particle_system >             m_systems;
+		handle_store< particle_system_group_info, particle_system_group > m_groups;
+
 		hash_store< shash64, uint32 >                         m_names;
 		vector< particle_system_data >                        m_data; 
Index: /trunk/nv/gl/gl_context.hh
===================================================================
--- /trunk/nv/gl/gl_context.hh	(revision 498)
+++ /trunk/nv/gl/gl_context.hh	(revision 499)
@@ -60,4 +60,7 @@
 		virtual void update( texture t, const void* data );
 		virtual void update( buffer b, const void* data, size_t offset, size_t size );
+		virtual void* map_buffer( buffer, buffer_access, size_t /*offset*/, size_t /*length*/ );
+		virtual void unmap_buffer( buffer );
+
 		//		virtual void update( buffer b, uint32 index, const void* data, size_t offset, size_t size );
 
Index: /trunk/nv/gl/gl_enum.hh
===================================================================
--- /trunk/nv/gl/gl_enum.hh	(revision 498)
+++ /trunk/nv/gl/gl_enum.hh	(revision 499)
@@ -43,4 +43,5 @@
 	unsigned int output_slot_to_enum( output_slot slot );
 
+	unsigned int buffer_access_to_bitfield( buffer_access type );
 	unsigned int datatype_to_gl_enum( datatype type );
 	datatype gl_enum_to_datatype( unsigned int gl_enum );
Index: /trunk/nv/interface/context.hh
===================================================================
--- /trunk/nv/interface/context.hh	(revision 498)
+++ /trunk/nv/interface/context.hh	(revision 499)
@@ -177,4 +177,7 @@
 		virtual void update( texture, const void* ) = 0;
 		virtual void update( buffer, const void*, size_t /*offset*/, size_t /*size*/ ) = 0;
+		virtual void* map_buffer( buffer, buffer_access, size_t /*offset*/, size_t /*length*/ ) = 0;
+		virtual void unmap_buffer( buffer ) = 0;
+
 
 //		virtual void update( buffer, uint32 /*index*/, const void* , size_t /*offset*/, size_t /*size*/ ) = 0;
Index: /trunk/nv/interface/device.hh
===================================================================
--- /trunk/nv/interface/device.hh	(revision 498)
+++ /trunk/nv/interface/device.hh	(revision 499)
@@ -152,4 +152,9 @@
 	};
 
+	enum buffer_access
+	{
+		WRITE_UNSYNCHRONIZED,
+	};
+
 	struct buffer_info
 	{
Index: /trunk/src/engine/particle_engine.cc
===================================================================
--- /trunk/src/engine/particle_engine.cc	(revision 498)
+++ /trunk/src/engine/particle_engine.cc	(revision 499)
@@ -470,5 +470,5 @@
 }
 
-nv::particle_system nv::particle_engine::create_system( const string_view& id )
+nv::particle_system nv::particle_engine::create_system( const string_view& id, particle_system_group group )
 {
 	auto it = m_names.find( id );
@@ -480,5 +480,5 @@
 	particle_system result = m_systems.create();
 	particle_system_info* info = m_systems.get( result );
-
+	info->group = group;
 	info->data     = data;
 	uint32 ecount = data->emmiter_count;
@@ -496,25 +496,40 @@
 	info->count = 0;
 	info->particles = new particle[ data->quota ];
-	info->quads     = new particle_quad[ data->quota ];
-
+	info->last_update = m_last_update;
+
+	return result;
+}
+
+void nv::particle_engine::prepare( particle_system_group group )
+{
+	particle_system_group_info* info = m_groups.get( group );
+	if ( info )
+	{
+		info->count = 0;
+	}
+}
+
+void nv::particle_engine::draw( particle_system_group group, const render_state& rs, const scene_state& ss )
+{
+	particle_system_group_info* info = m_groups.get( group );
+	if ( info )
+	{
+		m_context->draw( nv::TRIANGLES, rs, ss, info->local ? m_program_local : m_program_world, info->vtx_array, info->count * 6, 0 );
+	}
+}
+
+nv::particle_system_group nv::particle_engine::create_group( uint32 max_particles )
+{
+	particle_system_group result     = m_groups.create();
+	particle_system_group_info* info = m_groups.get( result );
+	info->local = false;
+	info->count = 0;
+	info->quota = max_particles;
+	info->vtx_buffer = m_device->create_buffer( VERTEX_BUFFER, STREAM_DRAW, info->quota * sizeof( particle_quad )/*, info->quads_[0].data*/ );
 	vertex_array_desc desc;
-	info->vtx_buffer = m_device->create_buffer( VERTEX_BUFFER, STREAM_DRAW, data->quota * 6 * sizeof( particle_vtx ), info->quads[0].data );
 	desc.add_vertex_buffers< particle_vtx >( info->vtx_buffer, true );
 	info->vtx_array = m_context->create_vertex_array( desc );
-	info->last_update = m_last_update;
-	info->test = false;
-//	result->m_own_va      = true;
-//	result->m_offset      = 0;
-
+	info->quads     = new particle_quad[info->quota];
 	return result;
-}
-
-void nv::particle_engine::draw( particle_system system, const render_state& rs, const scene_state& ss )
-{
-	particle_system_info* info = m_systems.get( system );
-	if ( info )
-	{
-		m_context->draw( nv::TRIANGLES, rs, ss, info->data->local ?  m_program_local : m_program_world, info->vtx_array, info->count * 6 );
-	}
 }
 
@@ -537,4 +552,6 @@
 	while ( m_systems.size() > 0 )
 		release( m_systems.get_handle( 0 ) );
+	while ( m_groups.size() > 0 )
+		release( m_groups.get_handle( 0 ) );
 	m_emmiters.clear();
 	m_affectors.clear();
@@ -551,22 +568,33 @@
 	{
 		delete[] info->particles;
+		m_systems.destroy( system );
+	}
+}
+
+void nv::particle_engine::release( particle_system_group group )
+{
+	particle_system_group_info* info = m_groups.get( group );
+	if ( info )
+	{
 		delete[] info->quads;
-		//if ( system->own_va ) 
 		m_context->release( info->vtx_array );
-		m_systems.destroy( system );
-	}
-}
-
-void nv::particle_engine::update( particle_system system, const scene_state& s, uint32 ms )
+		m_groups.destroy( group );
+	}
+}
+
+void nv::particle_engine::update( const scene_state& s, uint32 ms )
+{
+	m_last_update += ms;
+	m_view_matrix  = s.get_view();
+	m_model_matrix = s.get_model();
+	m_camera_pos   = s.get_camera().get_position();
+	m_inv_view_dir = math::normalize(-s.get_camera().get_direction());
+}
+
+void nv::particle_engine::update( particle_system system )
 {
 	particle_system_info* info = m_systems.get( system );
-	m_last_update += ms;
 	if ( info )
 	{
-		m_view_matrix  = s.get_view();
-		m_model_matrix = s.get_model();
-		m_camera_pos   = s.get_camera().get_position();
-		m_inv_view_dir = math::normalize(-s.get_camera().get_direction());
-
 		update_emmiters( info, m_last_update );
 		destroy_particles( info, m_last_update );
@@ -575,5 +603,4 @@
 
 		generate_data( info );
-		m_context->update( info->vtx_buffer, info->quads, /*system->m_offset*sizeof(particle_quad)*/ 0, info->count*sizeof(particle_quad) );
 	}
 }
@@ -585,15 +612,6 @@
 	{
 		vec2 texcoords[4] = { a, vec2( b.x, a.y ), vec2( a.x, b.y ), b };
-
-		for ( uint32 i = 0; i < info->data->quota; ++i )
-		{
-			particle_quad& rdata   = info->quads[i];
-			rdata.data[0].texcoord = texcoords[0];
-			rdata.data[1].texcoord = texcoords[1];
-			rdata.data[2].texcoord = texcoords[2];
-			rdata.data[3].texcoord = texcoords[3];
-			rdata.data[4].texcoord = texcoords[2];
-			rdata.data[5].texcoord = texcoords[1];
-		}
+		for ( uint32 i = 0; i < 4; ++i )
+			info->texcoords[i] = texcoords[i];
 	}
 }
@@ -601,4 +619,13 @@
 void nv::particle_engine::generate_data( particle_system_info* info )
 {
+//  	void* rawptr = m_context->map_buffer( info->vtx_buffer, nv::WRITE_UNSYNCHRONIZED, offset, info->count*sizeof( particle_quad ) );
+//  	particle_quad* quads = reinterpret_cast<particle_quad*>( rawptr );
+
+	particle_system_group_info* group = m_groups.get( info->group );
+	if ( !info )
+	{
+		return;
+	}
+
 	vec2 lb     = vec2( -0.5f, -0.5f );
 	vec2 rt     = vec2( 0.5f, 0.5f );
@@ -634,8 +661,17 @@
 	vec3 pdir( 0.0f, 1.0f, 0.0f );
 
+	vec2 texcoords[4] =
+	{
+		info->texcoords[0],
+		info->texcoords[1],
+		info->texcoords[2],
+		info->texcoords[3],
+	};
+
 	for ( uint32 i = 0; i < info->count; ++i )
 	{
 		const particle& pdata = info->particles[i];
-		particle_quad& rdata  = info->quads[i];
+//		particle_quad& rdata  = quads[i];
+		particle_quad& rdata  = group->quads[i + group->count];
 
 		vec3 view_dir( m_inv_view_dir );
@@ -676,17 +712,25 @@
 		rdata.data[0].position = pdata.position + s0;
 		rdata.data[0].color    = pdata.color;
+		rdata.data[0].texcoord = texcoords[0];
 
 		rdata.data[1].position = pdata.position + s1;
 		rdata.data[1].color    = pdata.color;
+		rdata.data[1].texcoord = texcoords[1];
 
 		rdata.data[2].position = pdata.position + s2;
 		rdata.data[2].color    = pdata.color;
+		rdata.data[2].texcoord = texcoords[2];
 
 		rdata.data[3].position = pdata.position + s3;
 		rdata.data[3].color    = pdata.color;
+		rdata.data[3].texcoord = texcoords[3];
 
 		rdata.data[4] = rdata.data[2];
 		rdata.data[5] = rdata.data[1];
 	}
+//	m_context->unmap_buffer( info->vtx_buffer );
+
+	m_context->update( group->vtx_buffer, group->quads, group->count*sizeof(particle_quad), info->count*sizeof( particle_quad ) );
+	group->count += info->count;
 }
 
Index: /trunk/src/gfx/debug_draw.cc
===================================================================
--- /trunk/src/gfx/debug_draw.cc	(revision 498)
+++ /trunk/src/gfx/debug_draw.cc	(revision 499)
@@ -45,13 +45,10 @@
 		if ( m_va.is_valid() ) m_context->release( m_va );
 		m_buffer_size = nv::max( m_data.size(), 4096U, m_buffer_size );
-		m_vb = m_context->get_device()->create_buffer( VERTEX_BUFFER, nv::STREAM_DRAW, m_buffer_size * sizeof( debug_vtx ), m_data.data() );
+		m_vb = m_context->get_device()->create_buffer( VERTEX_BUFFER, nv::STREAM_DRAW, m_buffer_size * sizeof( debug_vtx ) );
 		vertex_array_desc va_desc;
 		va_desc.add_vertex_buffers< debug_vtx >( m_vb, true );
 		m_va = m_context->create_vertex_array( va_desc );
 	}
-	else
-	{
-		m_context->update( m_vb, m_data.data(), 0, m_data.size()* sizeof( debug_vtx ) );
-	}
+	m_context->update( m_vb, m_data.data(), 0, m_data.size()* sizeof( debug_vtx ) );
 }
 
Index: /trunk/src/gl/gl_context.cc
===================================================================
--- /trunk/src/gl/gl_context.cc	(revision 498)
+++ /trunk/src/gl/gl_context.cc	(revision 499)
@@ -428,4 +428,31 @@
 			glTexImage2D( gl_type, 0, static_cast<GLint>( nv::image_format_to_internal_enum(format.format) ), size.x, size.y, 0, nv::image_format_to_enum(format.format), nv::datatype_to_gl_enum(format.type), data );
 	}
+}
+
+void* nv::gl_context::map_buffer( buffer b, buffer_access ba, size_t offset, size_t length )
+{
+	const gl_buffer_info* info = static_cast<const gl_buffer_info*>( m_device->get_buffer_info( b ) );
+	unsigned int glenum = buffer_type_to_enum( info->type );
+	glBindBuffer( glenum, info->glid );
+	void* result = glMapBufferRange( glenum, GLintptr( offset ), GLsizeiptr( length ), buffer_access_to_bitfield( ba ) );
+	if ( result == nullptr )
+	{
+		while ( GLenum err = glGetError() )
+		switch ( err )
+		{
+		case GL_INVALID_VALUE     : NV_LOG_ERROR( "map_buffer failed : GL_INVALID_VALUE" ) break;
+		case GL_INVALID_OPERATION : NV_LOG_ERROR( "map_buffer failed : GL_INVALID_OPERATION " ) break;
+		case GL_OUT_OF_MEMORY     : NV_LOG_ERROR( "map_buffer failed : GL_OUT_OF_MEMORY " ) break;
+		default:
+			break;
+		}
+	}
+	return result;
+}
+
+void nv::gl_context::unmap_buffer( buffer b )
+{
+	const gl_buffer_info* info = static_cast<const gl_buffer_info*>( m_device->get_buffer_info( b ) );
+	glUnmapBuffer( buffer_type_to_enum( info->type ) );
 }
 
Index: /trunk/src/gl/gl_enum.cc
===================================================================
--- /trunk/src/gl/gl_enum.cc	(revision 498)
+++ /trunk/src/gl/gl_enum.cc	(revision 499)
@@ -287,5 +287,4 @@
 }
 
-
 unsigned int nv::output_slot_to_enum( output_slot slot )
 {
@@ -307,4 +306,13 @@
 }
 
+
+unsigned int nv::buffer_access_to_bitfield( buffer_access type )
+{
+	switch ( type )
+	{
+	case WRITE_UNSYNCHRONIZED     : return GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
+	default: return 0; // TODO: throw!
+	}
+}
 
 unsigned int nv::datatype_to_gl_enum( datatype type )
