Index: trunk/src/engine/particle_engine.cc
===================================================================
--- trunk/src/engine/particle_engine.cc	(revision 519)
+++ trunk/src/engine/particle_engine.cc	(revision 520)
@@ -1,3 +1,3 @@
-// Copyright (C) 2014-2015 ChaosForge Ltd
+// Copyright (C) 2014-2016 ChaosForge Ltd
 // http://chaosforge.org/
 //
@@ -26,5 +26,5 @@
 using namespace nv;
 
-static void nv_particle_emitter_point( const particle_emitter_data*, particle* p, uint32 count )
+static void nv_particle_emitter_point( random_base*, const particle_emitter_data*, particle* p, uint32 count )
 {
 	for ( uint32 i = 0; i < count; ++i )
@@ -34,35 +34,32 @@
 }
 
-static void nv_particle_emitter_box( const particle_emitter_data* pe, particle* p, uint32 count )
-{
-	random& r = random::get();
-	for ( uint32 i = 0; i < count; ++i )
-	{
-		p[i].position = 
-			r.frange( -pe->hextents[0], pe->hextents[0] ) * pe->cdir +
-			r.frange( 0.0f, pe->extents[1] ) * pe->dir +
-			r.frange( -pe->hextents[2], pe->hextents[2] ) * pe->odir;
-	}
-}
-
-static void nv_particle_emitter_cylinder( const particle_emitter_data* pe, particle* p, uint32 count )
-{
-	random& r = random::get();
-	for ( uint32 i = 0; i < count; ++i )
-	{
-		vec2 rellipse( r.disk_point( pe->precise ) * pe->extents[0] );
+static void nv_particle_emitter_box( random_base* r, const particle_emitter_data* pe, particle* p, uint32 count )
+{
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		p[i].position = 
+			r->frange( -pe->hextents[0], pe->hextents[0] ) * pe->cdir +
+			r->frange( 0.0f, pe->extents[1] ) * pe->dir +
+			r->frange( -pe->hextents[2], pe->hextents[2] ) * pe->odir;
+	}
+}
+
+static void nv_particle_emitter_cylinder( random_base* r, const particle_emitter_data* pe, particle* p, uint32 count )
+{
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		vec2 rellipse( r->disk_point( pe->precise ) * pe->extents[0] );
 		p[i].position = 
 			rellipse.x * pe->cdir +
-			r.frange( 0.0f, pe->extents[1] ) * pe->dir +
+			r->frange( 0.0f, pe->extents[1] ) * pe->dir +
 			rellipse.y * pe->odir;
 	}
 }
 
-static void nv_particle_emitter_sphere( const particle_emitter_data* pe, particle* p, uint32 count )
-{
-	random& r = random::get();
-	for ( uint32 i = 0; i < count; ++i )
-	{
-		vec3 rsphere = r.sphere_point( pe->precise ) * pe->extents[0];
+static void nv_particle_emitter_sphere( random_base* r, const particle_emitter_data* pe, particle* p, uint32 count )
+{
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		vec3 rsphere = r->sphere_point( pe->precise ) * pe->extents[0];
 		p[i].position = 
 			rsphere.x * pe->cdir +
@@ -72,23 +69,21 @@
 }
 
-static void nv_particle_emitter_cylindroid( const particle_emitter_data* pe, particle* p, uint32 count )
-{
-	random& r = random::get();
-	for ( uint32 i = 0; i < count; ++i )
-	{
-		vec2 rellipse = r.ellipse_point( vec2( pe->hextents[0], pe->hextents[2] ), pe->precise );
+static void nv_particle_emitter_cylindroid( random_base* r, const particle_emitter_data* pe, particle* p, uint32 count )
+{
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		vec2 rellipse = r->ellipse_point( vec2( pe->hextents[0], pe->hextents[2] ), pe->precise );
 		p[i].position = 
 			rellipse.x * pe->cdir +
-			r.frange( 0.0f, pe->extents[1] ) * pe->dir +
+			r->frange( 0.0f, pe->extents[1] ) * pe->dir +
 			rellipse.y * pe->odir;
 	}
 }
 
-static void nv_particle_emitter_ellipsoid( const particle_emitter_data* pe, particle* p, uint32 count )
-{
-	random& r = random::get();
-	for ( uint32 i = 0; i < count; ++i )
-	{
-		vec3 rsphere = r.ellipsoid_point( pe->hextents, pe->precise );
+static void nv_particle_emitter_ellipsoid( random_base* r, const particle_emitter_data* pe, particle* p, uint32 count )
+{
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		vec3 rsphere = r->ellipsoid_point( pe->hextents, pe->precise );
 		p[i].position = 
 			rsphere.x * pe->cdir +
@@ -98,10 +93,9 @@
 }
 
-static void nv_particle_emitter_hollow_cylinder( const particle_emitter_data* pe, particle* p, uint32 count )
-{
-	random& r = random::get();
-	for ( uint32 i = 0; i < count; ++i )
-	{
-		vec2 rellipse = r.hollow_disk_point( 
+static void nv_particle_emitter_hollow_cylinder( random_base* r, const particle_emitter_data* pe, particle* p, uint32 count )
+{
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		vec2 rellipse = r->hollow_disk_point(
 			pe->ihextents[0],
 			pe->hextents[0],
@@ -109,15 +103,14 @@
 		p[i].position = 
 			rellipse.x * pe->cdir +
-			r.frange( 0.0f, pe->extents[1] ) * pe->dir +
+			r->frange( 0.0f, pe->extents[1] ) * pe->dir +
 			rellipse.y * pe->odir;
 	}
 }
 
-static void nv_particle_emitter_hollow_sphere( const particle_emitter_data* pe, particle* p, uint32 count )
-{
-	random& r = random::get();
-	for ( uint32 i = 0; i < count; ++i )
-	{
-		vec3 rellipse = r.hollow_sphere_point( pe->ihextents[0], pe->hextents[0], pe->precise );
+static void nv_particle_emitter_hollow_sphere( random_base* r, const particle_emitter_data* pe, particle* p, uint32 count )
+{
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		vec3 rellipse = r->hollow_sphere_point( pe->ihextents[0], pe->hextents[0], pe->precise );
 		p[i].position = 
 			rellipse.x * pe->cdir +
@@ -127,10 +120,9 @@
 }
 
-static void nv_particle_emitter_hollow_cylindroid( const particle_emitter_data* pe, particle* p, uint32 count )
-{
-	random& r = random::get();
-	for ( uint32 i = 0; i < count; ++i )
-	{
-		vec2 rellipse = r.hollow_ellipse_point( 
+static void nv_particle_emitter_hollow_cylindroid( random_base* r, const particle_emitter_data* pe, particle* p, uint32 count )
+{
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		vec2 rellipse = r->hollow_ellipse_point(
 			vec2( pe->ihextents[0], pe->ihextents[2] ), 
 			vec2( pe->hextents[0], pe->hextents[2] ), 
@@ -138,15 +130,14 @@
 		p[i].position = 
 			rellipse.x * pe->cdir +
-			r.frange( 0.0f, pe->extents[1] ) * pe->dir +
+			r->frange( 0.0f, pe->extents[1] ) * pe->dir +
 			rellipse.y * pe->odir;
 	}
 }
 
-static void nv_particle_emitter_hollow_ellipsoid( const particle_emitter_data* pe, particle* p, uint32 count )
-{
-	random& r = random::get();
-	for ( uint32 i = 0; i < count; ++i )
-	{
-		vec3 rellipse = r.hollow_ellipsoid_point( pe->ihextents, pe->hextents, pe->precise );
+static void nv_particle_emitter_hollow_ellipsoid( random_base* r, const particle_emitter_data* pe, particle* p, uint32 count )
+{
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		vec3 rellipse = r->hollow_ellipsoid_point( pe->ihextents, pe->hextents, pe->precise );
 		p[i].position = 
 			rellipse.x * pe->cdir +
@@ -278,12 +269,16 @@
 }
 
-nv::particle_engine::particle_engine( context* a_context, bool debug_data )
-{
-	m_context       = a_context;
+nv::particle_engine::particle_engine( context* a_context, random_base* rng, particle_group_manager* groups, bool debug_data )
+{
+	m_context = a_context;
+	m_pgm     = groups;
+	m_rng     = rng;
+	if ( m_rng == nullptr )
+		m_rng = &random::get();
+
 	if ( debug_data )
 	{
 		m_debug_emitter_names  = new nv::hash_map< nv::particle_emitter_func,  nv::const_string >;
 		m_debug_affector_names = new nv::hash_map< nv::particle_affector_func, nv::const_string >;
-		m_debug_affector_types = new nv::hash_map< nv::particle_affector_func, nv::type_entry* >;
 		m_debug_emitter_list   = new nv::vector< nv::particle_emitter_func >;
 		m_debug_affector_list  = new nv::vector< nv::particle_affector_func >;
@@ -294,9 +289,9 @@
 }
 
-nv::particle_system nv::particle_engine::create_system( const particle_system_data* data, particle_system_group group )
-{
-	particle_system_group_info* ginfo = m_groups.get( group );
-	if ( !ginfo ) return nv::particle_system();
-
+nv::particle_system nv::particle_engine::create_system( const particle_system_data* data, particle_group group )
+{
+	if ( !m_pgm->ref( group ) ) 
+		return nv::particle_system();
+	
 	particle_system result = m_systems.create();
 	particle_system_info* info = m_systems.get( result );
@@ -310,5 +305,5 @@
 			info->emitters[i].pause = 0;
 		else
-			info->emitters[i].pause = random::get().frange( data->emitters[i].duration_min, data->emitters[i].duration_max );
+			info->emitters[i].pause = m_rng->frange( data->emitters[i].duration_min, data->emitters[i].duration_max );
 
 	}
@@ -316,10 +311,9 @@
 	info->count = 0;
 	info->particles = new particle[data->quota];
-	ginfo->ref_counter++;
 
 	return result;
 }
 
-nv::particle_system nv::particle_engine::create_system( resource< nv::particle_system_data > rdata, particle_system_group group )
+nv::particle_system nv::particle_engine::create_system( resource< nv::particle_system_data > rdata, particle_group group )
 {
 	if ( auto data = rdata.lock() )
@@ -328,27 +322,12 @@
 }
 
-void nv::particle_engine::prepare( particle_system_group group )
-{
-	particle_system_group_info* info = m_groups.get( group );
-	if ( info )
-	{
-		info->count = 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_context->create_buffer( VERTEX_BUFFER, STREAM_DRAW, info->quota * sizeof( particle_quad )/*, info->quads_[0].data*/ );
-	vertex_array_desc desc;
-	desc.add_vertex_buffers< particle_vtx >( info->vtx_buffer, true );
-	info->vtx_array = m_context->create_vertex_array( desc );
-	info->quads     = new particle_quad[info->quota];
-	info->ref_counter = 0;
-	return result;
+void nv::particle_engine::prepare( particle_group group )
+{
+	m_pgm->prepare( group );
+}
+
+nv::particle_group nv::particle_engine::create_group( uint32 max_particles )
+{
+	return m_pgm->create_group( max_particles );
 }
 
@@ -369,6 +348,6 @@
 	while ( m_systems.size() > 0 )
 		release( m_systems.get_handle( 0 ) );
-	while ( m_groups.size() > 0 )
-		release( m_groups.get_handle( 0 ) );
+	if ( m_pgm )
+		m_pgm->reset();
 	m_emitters.clear();
 	m_affectors.clear();
@@ -381,20 +360,13 @@
 	if ( info )
 	{
-		particle_system_group_info* ginfo = m_groups.get( info->group );
-		if ( ginfo ) ginfo->ref_counter--;
-				delete[] info->particles;
+		m_pgm->unref( info->group );
+		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;
-		m_context->release( info->vtx_array );
-		m_groups.destroy( group );
-	}
+void nv::particle_engine::release( particle_group group )
+{
+	m_pgm->release( group );
 }
 
@@ -403,7 +375,5 @@
 	particle_system_info* info = m_systems.get( system );
 	if ( info )
-	{
-		generate_data( info, s );
-	}
+		m_pgm->generate_data( array_view< particle >( info->particles, info->count ), info->data, info->group, s );
 }
 
@@ -440,126 +410,4 @@
 		info->texcoords[1] = b;
 	}
-}
-
-void nv::particle_engine::generate_data( particle_system_info* info, const scene_state& s )
-{
-//  	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 );
-
-	switch ( info->data->origin )
-	{
-	case particle_origin::CENTER        : break;
-	case particle_origin::TOP_LEFT      : lb = vec2(0.f,-1.f); rt = vec2(1.f,0.f);  break;
-	case particle_origin::TOP_CENTER    : lb.y = -1.f; rt.y = 0.f; break;
-	case particle_origin::TOP_RIGHT     : lb = vec2(-1.f,-1.f); rt = vec2(); break;
-	case particle_origin::CENTER_LEFT   : lb.x = 0.f; rt.x = 1.f; break;
-	case particle_origin::CENTER_RIGHT  : lb.x = -1.f; rt.x = 0.f; break;
-	case particle_origin::BOTTOM_LEFT   : lb = vec2(); rt = vec2(1.f,1.f); break;
-	case particle_origin::BOTTOM_CENTER : lb.y = 0.f; rt.y = 1.f; break; 
-	case particle_origin::BOTTOM_RIGHT  : lb = vec2(-1.f,0.f); rt = vec2(.0f,1.f); break;
-	}
-
-	const vec3 sm[4] = 
-	{ 
-		vec3( lb.x, lb.y, 0.0f ),
-		vec3( rt.x, lb.y, 0.0f ),
-		vec3( lb.x, rt.y, 0.0f ),
-		vec3( rt.x, rt.y, 0.0f ),
-	};
-	vec3 z( 0.0f, 0.0f ,1.0f );
-
-	particle_orientation orientation = info->data->orientation;
-	vec3 common_up ( info->data->common_up );
-	vec3 common_dir( info->data->common_dir );
-	bool accurate_facing = info->data->accurate_facing;
-	mat3 rot_mat;
-	vec3 right;
-	vec3 pdir( 0.0f, 1.0f, 0.0f );
-
-	vec3 camera_pos   = s.get_camera().get_position();
-	vec3 inv_view_dir = math::normalize( -s.get_camera().get_direction() );
-
-	for ( uint32 i = 0; i < info->count; ++i )
-	{
-		const particle& pdata = info->particles[i];
-//		particle_quad& rdata  = quads[i];
-		particle_quad& rdata  = group->quads[i + group->count];
-
-		vec3 view_dir( inv_view_dir );
-		if ( accurate_facing ) view_dir = math::normalize( camera_pos - pdata.position );
-
-		switch ( orientation )
-		{
-		case particle_orientation::POINT :
-			right   = math::normalize( math::cross( view_dir, vec3( 0, 1, 0 ) ) );
-			right   = math::rotate( right, pdata.rotation, view_dir );
-			rot_mat = mat3( right, math::cross( right, -view_dir ), -view_dir );
-			break;
-		case particle_orientation::ORIENTED :
-			pdir    = normalize_safe( pdata.velocity, pdir );
-			right   = math::normalize( math::cross( pdir, view_dir ) );
-			rot_mat = mat3( right, pdir, math::cross( pdir, right ) );
-			break;
-		case particle_orientation::ORIENTED_COMMON :
-			right   = math::normalize( math::cross( common_dir, view_dir ) );
-			rot_mat = mat3( right, common_dir, math::cross( common_dir, right ) );
-			break;
-		case particle_orientation::PERPENDICULAR :
-			pdir    = normalize_safe( pdata.velocity, pdir );
-			right   = math::normalize( math::cross( common_up, pdir ) );
-			rot_mat = mat3( right, common_up, math::cross( common_up, right ) );
-			break;
-		case particle_orientation::PERPENDICULAR_COMMON :
-			right   = math::normalize( math::cross( common_up, common_dir ) );
-			rot_mat = mat3( right, common_up, math::cross( common_up, right ) );
-			break;
-		}
-
-		vec2 texcoords[4] =
-		{
-			pdata.tcoord_a,
-			vec2( pdata.tcoord_b.x, pdata.tcoord_a.y ),
-			vec2( pdata.tcoord_a.x, pdata.tcoord_b.y ),
-			pdata.tcoord_b
-		};
-
-		vec3 size( pdata.size.x, pdata.size.y, 0.0f );
-		vec3 s0 = rot_mat * ( ( size * sm[0] ) );
-		vec3 s1 = rot_mat * ( ( size * sm[1] ) );
-		vec3 s2 = rot_mat * ( ( size * sm[2] ) );
-		vec3 s3 = rot_mat * ( ( size * sm[3] ) );
-
-		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;
 }
 
@@ -584,5 +432,4 @@
 	if ( ecount == 0 ) return;
 
-	random& r = random::get();
 	bool local = model.is_identity();
 // 	if ( !local ) 
@@ -605,5 +452,5 @@
 				{
 					particle& pinfo = info->particles[info->count];
-					edata.emitter_func( &(info->data->emitters[i]), &pinfo, 1 );
+					edata.emitter_func( m_rng, &(info->data->emitters[i]), &pinfo, 1 );
 					pinfo.position = vec3();
 					pinfo.position+= edata.position;
@@ -611,14 +458,14 @@
 						pinfo.position = pinfo.position * model;
 					pinfo.color    = edata.color_min == edata.color_max ?
-						edata.color_min : r.range( edata.color_min, edata.color_max );
+						edata.color_min : m_rng->range( edata.color_min, edata.color_max );
 					pinfo.size     = edata.size_min == edata.size_max ?
-						edata.size_min : r.range( edata.size_min, edata.size_max );
+						edata.size_min : m_rng->range( edata.size_min, edata.size_max );
 					if ( edata.square ) pinfo.size.y = pinfo.size.x;
 					float velocity = edata.velocity_min == edata.velocity_max ?
-						edata.velocity_min : r.frange( edata.velocity_min, edata.velocity_max );
+						edata.velocity_min : m_rng->frange( edata.velocity_min, edata.velocity_max );
 					pinfo.lifetime = dtime + einfo.next;
 					pinfo.death = ( edata.lifetime_min == edata.lifetime_max ?
-						edata.lifetime_min : r.frange( edata.lifetime_min, edata.lifetime_max ) );
-					pinfo.rotation = r.frand( 2* math::pi<float>() );
+						edata.lifetime_min : m_rng->frange( edata.lifetime_min, edata.lifetime_max ) );
+					pinfo.rotation = m_rng->frand( 2* math::pi<float>() );
 					pinfo.tcoord_a = info->texcoords[0];
 					pinfo.tcoord_b = info->texcoords[1];
@@ -628,7 +475,7 @@
 					{
 						float emission_angle = math::radians( edata.angle );
-						float cos_theta = r.frange( cos( emission_angle ), 1.0f );
+						float cos_theta = m_rng->frange( cos( emission_angle ), 1.0f );
 						float sin_theta = sqrt(1.0f - cos_theta * cos_theta );
-						float phi       = r.frange( 0.0f, 2* math::pi<float>() );
+						float phi       = m_rng->frange( 0.0f, 2* math::pi<float>() );
 						pinfo.velocity  = model.get_orientation() * 
 							( edata.odir * ( cos(phi) * sin_theta ) +
@@ -672,5 +519,4 @@
 	uint32 ecount = info->data->emitter_count;
 	if ( ecount == 0 ) return;
-	random& r = random::get();
 
 	for ( uint32 i = 0; i < ecount; ++i )
@@ -691,5 +537,5 @@
 				einfo.active = false;
 				if ( edata.repeat_min > 0.0f )
-					einfo.pause += r.frange( edata.repeat_min, edata.repeat_max );
+					einfo.pause += m_rng->frange( edata.repeat_min, edata.repeat_max );
 				else
 					einfo.pause = 0.0f;
@@ -698,5 +544,5 @@
 			{
 				einfo.active = true;
-				einfo.pause += r.frange( edata.duration_min, edata.duration_max );
+				einfo.pause += m_rng->frange( edata.duration_min, edata.duration_max );
 			}
 		}
@@ -776,12 +622,7 @@
 }
 
-nv::particle_render_data nv::particle_engine::get_render_data( particle_system_group group )
-{
-	const particle_system_group_info* info = m_groups.get( group );
-	if ( info )
-	{
-		return{ info->count, info->vtx_array };
-	}
-	return { 0, nv::vertex_array() };
+nv::particle_render_data nv::particle_engine::get_render_data( particle_group group )
+{
+	return m_pgm->get_render_data( group );
 }
 
@@ -794,5 +635,16 @@
 }
 
-void nv::particle_engine::register_types( type_database* db )
+const nv::type_entry* nv::particle_engine::get_debug_type( particle_affector_func f )
+{
+	if ( m_debug_affector_types )
+	{
+		auto it = m_debug_affector_types->find( f );
+		if ( it != m_debug_affector_types->end() )
+			return it->second;
+	}
+	return nullptr;
+}
+
+void nv::particle_engine::register_types( type_database* db, bool debug_data )
 {
 	db->create_type<particle_orientation>()
@@ -816,6 +668,9 @@
 		;
 
-	if ( m_debug_affector_types )
-	{
+	if ( debug_data )
+	{
+		if ( !m_debug_affector_types )
+			m_debug_affector_types = new nv::hash_map< nv::particle_affector_func, nv::type_entry* >;
+
 		int you_could_get_rid_of_the_init_function_using_these; int error;
 		int universal_vm_based_affector;
@@ -849,2 +704,3 @@
 	}
 }
+
