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 )
