Index: trunk/src/engine/particle_engine.cc
===================================================================
--- trunk/src/engine/particle_engine.cc	(revision 499)
+++ trunk/src/engine/particle_engine.cc	(revision 500)
@@ -20,11 +20,13 @@
 	"varying vec4 v_color;\n"
 	"varying vec2 v_texcoord;\n"
+	"varying vec3 v_position;\n"
 	"uniform mat4 nv_m_view;\n"
 	"uniform mat4 nv_m_projection;\n"
 	"void main(void)\n"
 	"{\n"
-	"	gl_Position = nv_m_projection * nv_m_view * vec4 (nv_position, 1.0);\n"
-	"	v_texcoord  = nv_texcoord;\n"
-	"	v_color     = nv_color;\n"
+	"	v_position    = nv_position;\n"
+	"	v_texcoord    = nv_texcoord;\n"
+	"	v_color       = nv_color;\n"
+	"	gl_Position   = nv_m_projection * nv_m_view * vec4 (nv_position, 1.0);\n"
 	"}\n";
 static const char *nv_particle_engine_vertex_shader_local =
@@ -35,10 +37,12 @@
 	"varying vec4 v_color;\n"
 	"varying vec2 v_texcoord;\n"
+	"varying vec3 v_position;\n"
 	"uniform mat4 nv_m_mvp;\n"
 	"void main(void)\n"
 	"{\n"
-	"	gl_Position = nv_m_mvp * vec4 (nv_position, 1.0);\n"
-	"	v_texcoord  = nv_texcoord;\n"
-	"	v_color     = nv_color;\n"
+	"	v_position    = nv_position;\n"
+	"	v_texcoord    = nv_texcoord;\n"
+	"	v_color       = nv_color;\n"
+	"	gl_Position   = nv_m_mvp * vec4 (nv_position, 1.0);\n"
 	"}\n";
 static const char *nv_particle_engine_fragment_shader =
@@ -47,8 +51,10 @@
 	"varying vec4 v_color;\n"
 	"varying vec2 v_texcoord;\n"
+	"varying vec3 v_position;\n"
 	"void main(void)\n"
 	"{\n"
 	"	vec4 tex_color = texture2D( nv_t_diffuse, v_texcoord );\n"
-	"	gl_FragColor   = v_color * tex_color;\n"
+	"	float edge = smoothstep( 0.0, 0.1, v_position.y );\n"
+	"	gl_FragColor   = v_color * tex_color * edge;\n"
 	"}\n";
 
@@ -205,5 +211,5 @@
 	if ( datap->average )
 	{
-		float norm_factor = nv::min( factor, 1.0f );
+		float norm_factor = nv::min( factor, 1.0f, p->lifetime );
 		for ( uint32 i = 0; i < count; ++i ) 
 			p[i].velocity = datap->force_vector * norm_factor + p[i].velocity * ( 1.0f - norm_factor );
@@ -211,5 +217,5 @@
 	else
 	{
-		vec3 scvector = datap->force_vector * factor;
+		vec3 scvector = datap->force_vector * nv::min( factor, p->lifetime );
 		for ( uint32 i = 0; i < count; ++i ) p[i].velocity += scvector;
 	}
@@ -241,5 +247,5 @@
 	{
 		particle& pt = p[i];
-		vec3 direction  = pt.velocity * factor;
+		vec3 direction  = pt.velocity * nv::min( factor, p->lifetime );
 		if ( math::dot( datap->plane_normal, pt.position + direction ) + datap->distance <= 0.0f )
 		{
@@ -270,5 +276,5 @@
 {
 	const nvpe_color_fader_data* datap = reinterpret_cast<const nvpe_color_fader_data*>( data->paramters );
-	vec4 adjustment = datap->adjustment * factor;
+	vec4 adjustment = datap->adjustment * nv::min( factor, p->lifetime );
 	for ( uint32 i = 0; i < count; ++i )
 	{
@@ -293,5 +299,5 @@
 {
 	const nvpe_scaler_data* datap = reinterpret_cast<const nvpe_scaler_data*>( data->paramters );
-	vec2 adjustment = datap->adjustment * factor;
+	vec2 adjustment = datap->adjustment * nv::min( factor, p->lifetime );
 	for ( uint32 i = 0; i < count; ++i )
 	{
@@ -399,12 +405,12 @@
 				edata.velocity_max = element.get<float>("velocity_max", velocity );
 				float lifetime     = element.get<float>("lifetime", 1.0f );
-				edata.lifetime_min = uint32( element.get<float>("lifetime_min", lifetime ) * 1000.f );
-				edata.lifetime_max = uint32( element.get<float>("lifetime_max", lifetime ) * 1000.f );
+				edata.lifetime_min = element.get<float>("lifetime_min", lifetime );
+				edata.lifetime_max = element.get<float>("lifetime_max", lifetime );
 				float duration     = element.get<float>("duration", 0.0f );
-				edata.duration_min = uint32( element.get<float>("duration_min", duration ) * 1000.f );
-				edata.duration_max = uint32( element.get<float>("duration_max", duration ) * 1000.f );
+				edata.duration_min = element.get<float>("duration_min", duration );
+				edata.duration_max = element.get<float>("duration_max", duration );
 				float repeat       = element.get<float>("repeat_delay", 0.0f );
-				edata.repeat_min   = uint32( element.get<float>("repeat_delay_min", repeat ) * 1000.f );
-				edata.repeat_max   = uint32( element.get<float>("repeat_delay_max", repeat ) * 1000.f );
+				edata.repeat_min   = element.get<float>("repeat_delay_min", repeat );
+				edata.repeat_max   = element.get<float>("repeat_delay_max", repeat );
 
 				edata.rate         = element.get<float>("rate", 1.0f );
@@ -464,5 +470,4 @@
 	m_program_local = m_device->create_program( nv_particle_engine_vertex_shader_local, nv_particle_engine_fragment_shader );
 	m_program_world = m_device->create_program( nv_particle_engine_vertex_shader_world, nv_particle_engine_fragment_shader );
-	m_last_update = 0;
 
 	register_standard_emmiters();
@@ -472,4 +477,7 @@
 nv::particle_system nv::particle_engine::create_system( const string_view& id, particle_system_group group )
 {
+	particle_system_group_info* ginfo = m_groups.get( group );
+	if ( !ginfo ) return nv::particle_system();
+
 	auto it = m_names.find( id );
 	if ( it == m_names.end() )
@@ -486,9 +494,8 @@
 	{
 		info->emmiters[i].active      = true;
-		info->emmiters[i].last_create = float( m_last_update );
-		if ( data->emmiters[i].duration_max == 0 )
-			info->emmiters[i].next_toggle = 0;
+		if ( data->emmiters[i].duration_max == 0.0f )
+			info->emmiters[i].pause = 0;
 		else
-			info->emmiters[i].next_toggle = m_last_update + random::get().urange( data->emmiters[i].duration_min, data->emmiters[i].duration_max );
+			info->emmiters[i].pause = random::get().frange( data->emmiters[i].duration_min, data->emmiters[i].duration_max );
 
 	}
@@ -496,5 +503,5 @@
 	info->count = 0;
 	info->particles = new particle[ data->quota ];
-	info->last_update = m_last_update;
+	ginfo->ref_counter++;
 
 	return result;
@@ -531,4 +538,5 @@
 	info->vtx_array = m_context->create_vertex_array( desc );
 	info->quads     = new particle_quad[info->quota];
+	info->ref_counter = 0;
 	return result;
 }
@@ -546,4 +554,9 @@
 	register_standard_emmiters();
 	register_standard_affectors();
+}
+
+bool nv::particle_engine::loaded( const string_view& system_id )
+{
+	return m_names.find( system_id ) != m_names.end();
 }
 
@@ -558,5 +571,4 @@
 	m_names.clear();
 	m_data.clear();
-	m_last_update = 0;
 }
 
@@ -567,5 +579,7 @@
 	if ( info )
 	{
-		delete[] info->particles;
+		particle_system_group_info* ginfo = m_groups.get( info->group );
+		if ( ginfo ) ginfo->ref_counter--;
+				delete[] info->particles;
 		m_systems.destroy( system );
 	}
@@ -583,39 +597,48 @@
 }
 
-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 )
+void nv::particle_engine::render( particle_system system, const scene_state& s )
 {
 	particle_system_info* info = m_systems.get( system );
 	if ( info )
 	{
-		update_emmiters( info, m_last_update );
-		destroy_particles( info, m_last_update );
-		create_particles( info, m_last_update );
-		update_particles( info, m_last_update );
-
-		generate_data( info );
-	}
-}
-
-void nv::particle_engine::set_texcoords( particle_system system, vec2 a, vec2 b )
+		generate_data( info, s );
+	}
+}
+
+void nv::particle_engine::update( particle_system system, float dtime )
 {
 	particle_system_info* info = m_systems.get( system );
 	if ( info )
 	{
-		vec2 texcoords[4] = { a, vec2( b.x, a.y ), vec2( a.x, b.y ), b };
-		for ( uint32 i = 0; i < 4; ++i )
-			info->texcoords[i] = texcoords[i];
-	}
-}
-
-void nv::particle_engine::generate_data( particle_system_info* info )
+// 		while ( dtime > 0.2 )
+// 		{
+// 			update_emmiters( info, 0.2 );
+// 			destroy_particles( info, 0.2 );
+// 			create_particles( info, 0.2 );
+// 			update_particles( info, 0.2 );
+// 			dtime -= 0.2;
+//		}
+
+		update_emmiters( info, dtime );
+		destroy_particles( info, dtime );
+		create_particles( info, dtime );
+		update_particles( info, dtime );
+
+//		generate_data( info );
+	}
+}
+
+void nv::particle_engine::set_texcoords( particle_system system, vec2 a, vec2 b )
+{
+
+	particle_system_info* info = m_systems.get( system );
+	if ( info )
+	{
+		info->texcoords[0] = a;
+		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 ) );
@@ -661,11 +684,6 @@
 	vec3 pdir( 0.0f, 1.0f, 0.0f );
 
-	vec2 texcoords[4] =
-	{
-		info->texcoords[0],
-		info->texcoords[1],
-		info->texcoords[2],
-		info->texcoords[3],
-	};
+	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 )
@@ -675,6 +693,6 @@
 		particle_quad& rdata  = group->quads[i + group->count];
 
-		vec3 view_dir( m_inv_view_dir );
-		if ( accurate_facing ) view_dir = math::normalize( m_camera_pos - pdata.position );
+		vec3 view_dir( inv_view_dir );
+		if ( accurate_facing ) view_dir = math::normalize( camera_pos - pdata.position );
 
 		switch ( orientation )
@@ -682,4 +700,5 @@
 		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;
@@ -704,4 +723,12 @@
 		}
 
+		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] ) );
@@ -735,5 +762,5 @@
 }
 
-void nv::particle_engine::destroy_particles( particle_system_info* info, uint32 ms )
+void nv::particle_engine::destroy_particles( particle_system_info* info, float dtime )
 {
 	if ( info->count > 0 )
@@ -741,6 +768,6 @@
 		{
 			particle& pinfo = info->particles[i];
-			if ( //pdata.position.y < 0.0f ||
-				ms >= pinfo.death )
+			pinfo.lifetime += dtime;
+			if ( pinfo.lifetime >= pinfo.death )
 			{
 				info->count--;
@@ -750,5 +777,5 @@
 }
 
-void nv::particle_engine::create_particles( particle_system_info* info, uint32 ms )
+void nv::particle_engine::create_particles( particle_system_info* info, float dtime )
 {
 	uint32 ecount = info->data->emmiter_count;
@@ -759,11 +786,9 @@
 	mat3 orient;
 	bool local = info->data->local;
-	if ( !local ) 
-	{
-		source = vec3( m_model_matrix[3] );
-		orient = mat3( m_model_matrix );
-	}
-
-	float fms = float(ms);
+// 	if ( !local ) 
+// 	{
+// 		source = vec3( m_model_matrix[3] );
+// 		orient = mat3( m_model_matrix );
+// 	}
 
 	for ( uint32 i = 0; i < ecount; ++i )
@@ -773,6 +798,7 @@
 		if ( einfo.active )
 		{
-			float period = 1000.f / edata.rate;
-			while ( fms - einfo.last_create > period )
+			einfo.next -= dtime;
+			float fperiod = 1.0f / edata.rate;
+			while ( einfo.next < 0.0f )
 			{
 				if ( info->count < info->data->quota-1 )
@@ -780,17 +806,20 @@
 					particle& pinfo = info->particles[info->count];
 					edata.emmiter_func( &(info->data->emmiters[i]), &pinfo, 1 );
-
-					if ( !local ) pinfo.position  = orient * pinfo.position + source;
-					pinfo.position += edata.position;
-					pinfo.color     = edata.color_min == edata.color_max ? 
+					pinfo.position = vec3();
+//					if ( !local ) pinfo.position  = orient * pinfo.position + source;
+					pinfo.position+= edata.position;
+					pinfo.color    = edata.color_min == edata.color_max ? 
 						edata.color_min : r.range( edata.color_min, edata.color_max );
-					pinfo.size      = edata.size_min == edata.size_max ?
+					pinfo.size     = edata.size_min == edata.size_max ?
 						edata.size_min : r.range( edata.size_min, edata.size_max );
 					if ( edata.square ) pinfo.size.y = pinfo.size.x;
-					float velocity  = edata.velocity_min == edata.velocity_max ?
+					float velocity = edata.velocity_min == edata.velocity_max ?
 						edata.velocity_min : r.frange( edata.velocity_min, edata.velocity_max );
-					pinfo.death     = ms + ( edata.lifetime_min == edata.lifetime_max ?
-						edata.lifetime_min : r.urange( edata.lifetime_min, edata.lifetime_max ) );
-					//pinfo.rotation = r.frand( 360.0f );
+					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>() );
+					pinfo.tcoord_a = info->texcoords[0];
+					pinfo.tcoord_b = info->texcoords[1];
 
 					pinfo.velocity = edata.dir;
@@ -811,15 +840,14 @@
 					info->count++;
 				}
-				einfo.last_create += period;
+				einfo.next += fperiod;
 			}
+
 		}
 	}
 }
 
-void nv::particle_engine::update_particles( particle_system_info* info, uint32 ms )
-{
-	uint32 ticks = ms - info->last_update;
-	if ( ticks == 0 ) return;
-	float factor  = 0.001f * ticks;
+void nv::particle_engine::update_particles( particle_system_info* info, float dtime )
+{
+	if ( dtime <= 0.0f ) return;
 
 	uint32 acount = info->data->affector_count;
@@ -827,5 +855,5 @@
 	{
 		const particle_affector_data* padata = &(info->data->affectors[i]);
-		padata->process( padata, info->particles, factor, info->count );
+		padata->process( padata, info->particles, dtime, info->count );
 	}
 
@@ -834,10 +862,10 @@
 	{
 		particle& pdata = info->particles[i];
+		float factor = min( dtime, pdata.lifetime );
 		pdata.position += pdata.velocity * factor;
 	}
-	info->last_update = ms;
-}
-
-void nv::particle_engine::update_emmiters( particle_system_info* info, uint32 ms )
+}
+
+void nv::particle_engine::update_emmiters( particle_system_info* info, float dtime )
 {
 	uint32 ecount = info->data->emmiter_count;
@@ -850,19 +878,24 @@
 		auto& einfo = info->emmiters[i];
 
-		if ( einfo.next_toggle != 0 && ms >= einfo.next_toggle )
+		if ( einfo.pause > 0.0f )
+		{
+			einfo.pause -= dtime;
+			if ( einfo.pause == 0.0f ) einfo.pause = -0.001f;
+		}
+
+		if ( einfo.pause < 0.0f )
 		{
 			if ( einfo.active )
 			{
 				einfo.active = false;
-				if ( edata.repeat_min > 0 )
-					einfo.next_toggle += r.urange( edata.repeat_min, edata.repeat_max );
+				if ( edata.repeat_min > 0.0f )
+					einfo.pause += r.frange( edata.repeat_min, edata.repeat_max );
 				else
-					einfo.next_toggle = 0;
+					einfo.pause = 0.0f;
 			}
 			else
 			{
 				einfo.active = true;
-				einfo.last_create = float( einfo.next_toggle );
-				einfo.next_toggle += r.urange( edata.duration_min, edata.duration_max );
+				einfo.pause += r.frange( edata.duration_min, edata.duration_max );
 			}
 		}
@@ -896,4 +929,14 @@
 }
 
+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() };
+}
+
 void nv::particle_engine::register_standard_affectors()
 {
