Index: trunk/src/engine/default_resource_manager.cc
===================================================================
--- trunk/src/engine/default_resource_manager.cc	(revision 517)
+++ trunk/src/engine/default_resource_manager.cc	(revision 518)
@@ -21,4 +21,5 @@
 	m_gpu_materials = register_resource_handler< gpu_material >( new gpu_material_manager( context, m_materials, m_images ) );
 	m_models        = register_resource_handler< model >( new model_manager( this, m_binds, m_mesh_datas ) );
+	m_particles     = register_resource_handler< particle_system_data >( new particle_manager );
 }
 
@@ -26,4 +27,6 @@
 {
 	m_lua = lua;
+
+	particle_engine::register_types( const_cast< type_database* >( lua->get_type_data()->get_type_database() ) );
 
 	int below_already_registered;
@@ -70,4 +73,5 @@
 	m_animators->initialize( lua );
 	m_models->initialize( lua );
+	m_particles->initialize( lua );
 }
 
Index: trunk/src/engine/particle_engine.cc
===================================================================
--- trunk/src/engine/particle_engine.cc	(revision 517)
+++ trunk/src/engine/particle_engine.cc	(revision 518)
@@ -13,53 +13,14 @@
 #include <nv/core/logging.hh>
 
-static const char *nv_particle_engine_vertex_shader_world =
-	"#version 120\n"
-	"attribute vec3 nv_position;\n"
-	"attribute vec2 nv_texcoord;\n"
-	"attribute vec4 nv_color;\n"
-	"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"
-	"	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 =
-	"#version 120\n"
-	"attribute vec3 nv_position;\n"
-	"attribute vec2 nv_texcoord;\n"
-	"attribute vec4 nv_color;\n"
-	"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"
-	"	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 =
-	"#version 120\n"
-	"uniform sampler2D nv_t_diffuse;\n"
-	"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"
-	"	float edge = smoothstep( 0.0, 0.1, v_position.y );\n"
-	"	gl_FragColor   = v_color * tex_color * edge;\n"
-	"}\n";
+nv::hash_store< nv::shash64, nv::particle_emitter_func >   nv::particle_engine::m_emitters;
+nv::hash_store< nv::shash64, nv::particle_affector_funcs > nv::particle_engine::m_affectors;
+
+nv::hash_map< nv::particle_emitter_func,  nv::const_string >* nv::particle_engine::m_debug_emitter_names  = nullptr;
+nv::hash_map< nv::particle_affector_func, nv::const_string >* nv::particle_engine::m_debug_affector_names = nullptr;
+nv::hash_map< nv::particle_affector_func, nv::type_entry* >*  nv::particle_engine::m_debug_affector_types = nullptr;
 
 using namespace nv;
 
-static void nv_particle_emmiter_point( const particle_emmiter_data*, particle* p, uint32 count )
+static void nv_particle_emitter_point( const particle_emitter_data*, particle* p, uint32 count )
 {
 	for ( uint32 i = 0; i < count; ++i )
@@ -67,8 +28,7 @@
 		p[i].position = vec3();
 	}
-
-}
-
-static void nv_particle_emmiter_box( const particle_emmiter_data* pe, particle* p, uint32 count )
+}
+
+static void nv_particle_emitter_box( const particle_emitter_data* pe, particle* p, uint32 count )
 {
 	random& r = random::get();
@@ -82,5 +42,5 @@
 }
 
-static void nv_particle_emmiter_cylinder( const particle_emmiter_data* pe, particle* p, uint32 count )
+static void nv_particle_emitter_cylinder( const particle_emitter_data* pe, particle* p, uint32 count )
 {
 	random& r = random::get();
@@ -95,5 +55,5 @@
 }
 
-static void nv_particle_emmiter_sphere( const particle_emmiter_data* pe, particle* p, uint32 count )
+static void nv_particle_emitter_sphere( const particle_emitter_data* pe, particle* p, uint32 count )
 {
 	random& r = random::get();
@@ -108,5 +68,5 @@
 }
 
-static void nv_particle_emmiter_cylindroid( const particle_emmiter_data* pe, particle* p, uint32 count )
+static void nv_particle_emitter_cylindroid( const particle_emitter_data* pe, particle* p, uint32 count )
 {
 	random& r = random::get();
@@ -121,5 +81,5 @@
 }
 
-static void nv_particle_emmiter_ellipsoid( const particle_emmiter_data* pe, particle* p, uint32 count )
+static void nv_particle_emitter_ellipsoid( const particle_emitter_data* pe, particle* p, uint32 count )
 {
 	random& r = random::get();
@@ -134,5 +94,5 @@
 }
 
-static void nv_particle_emmiter_hollow_cylinder( const particle_emmiter_data* pe, particle* p, uint32 count )
+static void nv_particle_emitter_hollow_cylinder( const particle_emitter_data* pe, particle* p, uint32 count )
 {
 	random& r = random::get();
@@ -150,5 +110,5 @@
 }
 
-static void nv_particle_emmiter_hollow_sphere( const particle_emmiter_data* pe, particle* p, uint32 count )
+static void nv_particle_emitter_hollow_sphere( const particle_emitter_data* pe, particle* p, uint32 count )
 {
 	random& r = random::get();
@@ -163,5 +123,5 @@
 }
 
-static void nv_particle_emmiter_hollow_cylindroid( const particle_emmiter_data* pe, particle* p, uint32 count )
+static void nv_particle_emitter_hollow_cylindroid( const particle_emitter_data* pe, particle* p, uint32 count )
 {
 	random& r = random::get();
@@ -179,5 +139,5 @@
 }
 
-static void nv_particle_emmiter_hollow_ellipsoid( const particle_emmiter_data* pe, particle* p, uint32 count )
+static void nv_particle_emitter_hollow_ellipsoid( const particle_emitter_data* pe, particle* p, uint32 count )
 {
 	random& r = random::get();
@@ -306,203 +266,50 @@
 }
 
-void nv::particle_engine::load( lua::table_guard& table )
-{
-	shash64 id = table.get_string_hash_64( "id" );
-	if ( !id )
-	{
-		NV_LOG_ERROR( "Bad table passed to particle_engine!" )
-	}
-	// TODO : overwrite check
-	m_names[ id ] = m_data.size();
-
-	m_data.emplace_back();
-	auto& data = m_data.back();
-
-	data.quota   = table.get<uint32>("quota", 1024 );
-//	data.local   = table.get<bool>("local_space", false );
-	data.accurate_facing = table.get<bool>("accurate_facing", false );
-	data.emmiter_count   = 0;
-	data.affector_count  = 0;
-
-	const_string orientation = table.get_string( "orientation", "point" );
-	if ( orientation == "point" )                     { data.orientation = particle_orientation::POINT; }
-	else if ( orientation == "oriented" )             { data.orientation = particle_orientation::ORIENTED; }
-	else if ( orientation == "oriented_common" )      { data.orientation = particle_orientation::ORIENTED_COMMON; }
-	else if ( orientation == "perpendicular" )        { data.orientation = particle_orientation::PERPENDICULAR; }
-	else if ( orientation == "perpendicular_common" ) { data.orientation = particle_orientation::PERPENDICULAR_COMMON; }
-	else 
-	{
-		NV_LOG_ERROR( "Unknown orientation type! (", orientation, ")!" );
-		data.orientation = particle_orientation::POINT;
-	}
-
-	const_string origin = table.get_string( "origin", "center" );
-	if      ( origin == "center" )        { data.origin = particle_origin::CENTER; }
-	else if ( origin == "top_left" )      { data.origin = particle_origin::TOP_LEFT; }
-	else if ( origin == "top_center" )    { data.origin = particle_origin::TOP_CENTER; }
-	else if ( origin == "top_right" )     { data.origin = particle_origin::TOP_RIGHT; }
-	else if ( origin == "center_left" )   { data.origin = particle_origin::CENTER_LEFT; }
-	else if ( origin == "center_right" )  { data.origin = particle_origin::CENTER_RIGHT; }
-	else if ( origin == "bottom_left" )   { data.origin = particle_origin::BOTTOM_LEFT; }
-	else if ( origin == "bottom_center" ) { data.origin = particle_origin::BOTTOM_CENTER; }
-	else if ( origin == "bottom_right" )  { data.origin = particle_origin::BOTTOM_RIGHT; }
-	else 
-	{
-		NV_LOG_ERROR( "Unknown particle origin! (", origin, ")!" );
-		data.origin = particle_origin::CENTER;
-	}
-
-	data.common_up  = math::normalize( table.get<vec3>("common_up",  vec3(1,0,0) ) );
-	data.common_dir = math::normalize( table.get<vec3>("common_dir", vec3(0,1,0) ) );
-
-	vec2 def_size        = table.get<vec2>("size", vec2(0.1,0.1) );
-	uint32 elements = table.get_size();
-	for ( uint32 i = 0; i < elements; ++i )
-	{
-		lua::table_guard element( table, i+1 );
-		const_string type     = element.get_string( "type" );
-		const_string sub_type = element.get_string( "sub_type" );
-		if ( type == "emmiter" )
-		{
-			if ( data.emmiter_count < MAX_PARTICLE_EMMITERS )
-			{
-				particle_emmiter_data& edata = data.emmiters[ data.emmiter_count ];
-				auto emmiter_iter = m_emmiters.find( sub_type );
-				if ( emmiter_iter != m_emmiters.end() )
-				{
-					edata.emmiter_func = emmiter_iter->second;
-				}
-				else
-				{
-					edata.emmiter_func = nv_particle_emmiter_point;
-					NV_LOG_WARNING( "Unknown emmiter type in particle system! (", sub_type, ")" );
-				}
-
-				edata.position     = element.get<vec3>("position", vec3() );
-				edata.extents      = element.get<vec3>("extents", vec3(1,1,1) );
-				edata.extents[0]   = element.get<float>("width",  edata.extents[0] );
-				edata.extents[1]   = element.get<float>("depth",  edata.extents[1] );
-				edata.extents[2]   = element.get<float>("height", edata.extents[2] );
-				edata.extents[0]   = element.get<float>("radius",  edata.extents[0] );
-				edata.iextents     = element.get<vec3>("inner_extents", vec3() );
-				edata.iextents[0]  = element.get<float>("inner_width",  edata.iextents[0] );
-				edata.iextents[1]  = element.get<float>("inner_depth",  edata.iextents[1] );
-				edata.iextents[2]  = element.get<float>("inner_height", edata.iextents[2] );
-				edata.iextents[0]  = element.get<float>("inner_radius",  edata.iextents[0] );
-				edata.hextents     = 0.5f * edata.extents;
-				edata.ihextents    = 0.5f * edata.iextents;
-				edata.precise      = element.get<bool>("precise", false );
-				edata.square       = element.get<bool>("square", true );
-				vec4 color         = element.get<vec4>("color", vec4(1,1,1,1) );
-				edata.color_min    = element.get<vec4>("color_min", color );
-				edata.color_max    = element.get<vec4>("color_max", color );
-				vec2 size          = element.get<vec2>("size", def_size );
-				edata.size_min     = element.get<vec2>("size_min", size );
-				edata.size_max     = element.get<vec2>("size_max", size );
-				edata.angle        = element.get<float>("angle", 0.0f );
-				float velocity     = element.get<float>("velocity", 0.0f );
-				edata.velocity_min = element.get<float>("velocity_min", velocity );
-				edata.velocity_max = element.get<float>("velocity_max", velocity );
-				float lifetime     = element.get<float>("lifetime", 1.0f );
-				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 = 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   = 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 );
-				edata.dir          = math::normalize( element.get<vec3>("direction", vec3(0,1,0) ) );
-				
-				edata.odir = vec3( 0, 0, 1 );
-				if ( edata.dir != vec3( 0, 1, 0 ) && edata.dir != vec3( 0, -1, 0 ) )
-					edata.odir = math::normalize( math::cross( edata.dir, vec3( 0, 1, 0 ) ) );
-				edata.cdir = math::cross( edata.dir, edata.odir );
-
-				data.emmiter_count++;
-			}
-			else
-			{
-				NV_LOG_ERROR( "Too many emmiters (", MAX_PARTICLE_EMMITERS, " is MAX)!" );
-			}
-		}
-		else if ( type == "affector" )
-		{
-			if ( data.affector_count < MAX_PARTICLE_AFFECTORS )
-			{
-				particle_affector_data& adata = data.affectors[ data.affector_count ];
-				data.affector_count++;
-				auto affector_iter = m_affectors.find( sub_type );
-				if ( affector_iter != m_affectors.end() )
-				{
-					adata.process = affector_iter->second.process;
-					if ( !affector_iter->second.init( &element, &adata ) )
-					{
-						data.affector_count--;
-						NV_LOG_WARNING( "Bad data passed to ", sub_type, " affector in particle system!" );
-					}
-				}
-				else
-				{
-					data.affector_count--;
-					NV_LOG_WARNING( "Unknown affector type in particle system! (", sub_type, ")" );
-				}
-			}
-			else
-			{
-				NV_LOG_ERROR( "Too many affectors (", MAX_PARTICLE_AFFECTORS, " is MAX)!" );
-			}
-		}
-		else 
-		{
-			NV_LOG_WARNING( "Unknown element in particle system! (", type, ")" );
-		}
-	}
-
-}
-
-nv::particle_engine::particle_engine( context* a_context )
+nv::particle_engine::particle_engine( context* a_context, bool debug_data )
 {
 	m_context       = a_context;
-	m_program_local = m_context->create_program( nv_particle_engine_vertex_shader_local, nv_particle_engine_fragment_shader );
-	m_program_world = m_context->create_program( nv_particle_engine_vertex_shader_world, nv_particle_engine_fragment_shader );
-
-	register_standard_emmiters();
+	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* >;
+	}
+
+	register_standard_emitters();
 	register_standard_affectors();
 }
 
-nv::particle_system nv::particle_engine::create_system( const string_view& id, particle_system_group group )
+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();
 
-	auto it = m_names.find( id );
-	if ( it == m_names.end() )
-	{
-		return particle_system();
-	}
-	const particle_system_data* data = &(m_data[it->second]);
 	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;
+	info->data = data;
+	uint32 ecount = data->emitter_count;
 	for ( uint32 i = 0; i < ecount; ++i )
 	{
-		info->emmiters[i].active      = true;
-		if ( data->emmiters[i].duration_max == 0.0f )
-			info->emmiters[i].pause = 0;
+		info->emitters[i].active = true;
+		if ( data->emitters[i].duration_max == 0.0f )
+			info->emitters[i].pause = 0;
 		else
-			info->emmiters[i].pause = random::get().frange( data->emmiters[i].duration_min, data->emmiters[i].duration_max );
+			info->emitters[i].pause = random::get().frange( data->emitters[i].duration_min, data->emitters[i].duration_max );
 
 	}
 
 	info->count = 0;
-	info->particles = new particle[ data->quota ];
+	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 )
+{
+	if ( auto data = rdata.lock() )
+		return create_system( &*data, group );
+	return {};
 }
 
@@ -513,13 +320,4 @@
 	{
 		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 );
 	}
 }
@@ -544,6 +342,4 @@
 {
 	clear();
-	m_context->release( m_program_world );
-	m_context->release( m_program_local );
 }
 
@@ -551,11 +347,6 @@
 {
 	clear();
-	register_standard_emmiters();
+	register_standard_emitters( );
 	register_standard_affectors();
-}
-
-bool nv::particle_engine::loaded( const string_view& system_id )
-{
-	return m_names.find( system_id ) != m_names.end();
 }
 
@@ -566,8 +357,6 @@
 	while ( m_groups.size() > 0 )
 		release( m_groups.get_handle( 0 ) );
-	m_emmiters.clear();
+	m_emitters.clear();
 	m_affectors.clear();
-	m_names.clear();
-	m_data.clear();
 }
 
@@ -612,5 +401,5 @@
 // 		while ( dtime > 0.2 )
 // 		{
-// 			update_emmiters( info, 0.2 );
+// 			update_emitters( info, 0.2 );
 // 			destroy_particles( info, 0.2 );
 // 			create_particles( info, 0.2 );
@@ -619,5 +408,5 @@
 //		}
 
-		update_emmiters( info, dtime );
+		update_emitters( info, dtime );
 		destroy_particles( info, dtime );
 		create_particles( info, model, dtime );
@@ -778,5 +567,5 @@
 void nv::particle_engine::create_particles( particle_system_info* info, transform model, float dtime )
 {
-	uint32 ecount = info->data->emmiter_count;
+	uint32 ecount = info->data->emitter_count;
 	if ( ecount == 0 ) return;
 
@@ -791,6 +580,6 @@
 	for ( uint32 i = 0; i < ecount; ++i )
 	{
-		const auto& edata = info->data->emmiters[i];
-		auto& einfo = info->emmiters[i];
+		const auto& edata = info->data->emitters[i];
+		auto& einfo = info->emitters[i];
 		if ( einfo.active )
 		{
@@ -802,8 +591,8 @@
 				{
 					particle& pinfo = info->particles[info->count];
-					edata.emmiter_func( &(info->data->emmiters[i]), &pinfo, 1 );
+					edata.emitter_func( &(info->data->emitters[i]), &pinfo, 1 );
 					pinfo.position = vec3();
 					pinfo.position+= edata.position;
-					if ( !local )
+//					if ( !local )
 						pinfo.position = pinfo.position * model;
 					pinfo.color    = edata.color_min == edata.color_max ?
@@ -865,7 +654,7 @@
 }
 
-void nv::particle_engine::update_emmiters( particle_system_info* info, float dtime )
-{
-	uint32 ecount = info->data->emmiter_count;
+void nv::particle_engine::update_emitters( particle_system_info* info, float dtime )
+{
+	uint32 ecount = info->data->emitter_count;
 	if ( ecount == 0 ) return;
 	random& r = random::get();
@@ -873,6 +662,6 @@
 	for ( uint32 i = 0; i < ecount; ++i )
 	{
-		const auto& edata = info->data->emmiters[i];
-		auto& einfo = info->emmiters[i];
+		const auto& edata = info->data->emitters[i];
+		auto& einfo = info->emitters[i];
 
 		if ( einfo.pause > 0.0f )
@@ -902,27 +691,68 @@
 }
 
-void nv::particle_engine::register_emmiter_type( const string_view& name, particle_emmiter_func func )
-{
-	m_emmiters[ name ] = func;
-}
-
-void nv::particle_engine::register_standard_emmiters()
-{
-	register_emmiter_type( "point",             nv_particle_emmiter_point );
-	register_emmiter_type( "box",               nv_particle_emmiter_box );
-	register_emmiter_type( "cylinder",          nv_particle_emmiter_cylinder );
-	register_emmiter_type( "sphere",            nv_particle_emmiter_sphere );
-	register_emmiter_type( "cylindroid",        nv_particle_emmiter_cylindroid );
-	register_emmiter_type( "ellipsoid",         nv_particle_emmiter_ellipsoid );
-	register_emmiter_type( "hollow_cylinder",   nv_particle_emmiter_hollow_cylinder );
-	register_emmiter_type( "hollow_sphere",     nv_particle_emmiter_hollow_sphere );
-	register_emmiter_type( "hollow_cylindroid", nv_particle_emmiter_hollow_cylindroid );
-	register_emmiter_type( "hollow_ellipsoid",  nv_particle_emmiter_hollow_ellipsoid );
+void nv::particle_engine::register_emitter_type( const string_view& name, particle_emitter_func func )
+{
+	if ( m_debug_emitter_names )
+		( *m_debug_emitter_names )[func] = name;
+	m_emitters[ name ] = func;
+}
+
+void nv::particle_engine::register_standard_emitters()
+{
+	register_emitter_type( "point",             nv_particle_emitter_point );
+	register_emitter_type( "box",               nv_particle_emitter_box );
+	register_emitter_type( "cylinder",          nv_particle_emitter_cylinder );
+	register_emitter_type( "sphere",            nv_particle_emitter_sphere );
+	register_emitter_type( "cylindroid",        nv_particle_emitter_cylindroid );
+	register_emitter_type( "ellipsoid",         nv_particle_emitter_ellipsoid );
+	register_emitter_type( "hollow_cylinder",   nv_particle_emitter_hollow_cylinder );
+	register_emitter_type( "hollow_sphere",     nv_particle_emitter_hollow_sphere );
+	register_emitter_type( "hollow_cylindroid", nv_particle_emitter_hollow_cylindroid );
+	register_emitter_type( "hollow_ellipsoid",  nv_particle_emitter_hollow_ellipsoid );
 }
 
 void nv::particle_engine::register_affector_type( const string_view& name, particle_affector_init_func init, particle_affector_func process )
 {
+	if ( m_debug_affector_names )
+		( *m_debug_affector_names )[process] = name;
+
 	m_affectors[ name ].init    = init;
 	m_affectors[ name ].process = process;
+}
+
+nv::particle_emitter_func nv::particle_engine::get_emitter( shash64 emitter )
+{
+	auto emitter_iter = m_emitters.find( emitter );
+	if ( emitter_iter != m_emitters.end() )
+	{
+		return emitter_iter->second;
+	}
+	return nullptr;
+}
+
+nv::particle_affector_funcs nv::particle_engine::get_affector( shash64 affector )
+{
+	auto affector_iter = m_affectors.find( affector );
+	if ( affector_iter != m_affectors.end() )
+	{
+		return affector_iter->second;
+	}
+	return { nullptr, nullptr };
+}
+
+nv::string_view nv::particle_engine::get_debug_name( particle_emitter_func f )
+{
+	auto i = m_debug_emitter_names->find( f );
+	if ( i != m_debug_emitter_names->end() )
+		return i->second;
+	return {};
+}
+
+nv::string_view nv::particle_engine::get_debug_name( particle_affector_func f )
+{
+	auto i = m_debug_affector_names->find( f );
+	if ( i != m_debug_affector_names->end() )
+		return i->second;
+	return {};
 }
 
@@ -945,2 +775,53 @@
 }
 
+void nv::particle_engine::register_types( type_database* db )
+{
+	db->create_type<nv::particle_orientation>()
+		.value( "PS_POINT",                sint32( particle_orientation::POINT ),                "Point" )
+		.value( "PS_ORIENTED",             sint32( particle_orientation::ORIENTED ),             "Oriented" )
+		.value( "PS_ORIENTED_COMMON",      sint32( particle_orientation::ORIENTED_COMMON ),      "Oriented Common" )
+		.value( "PS_PERPENDICULAR",        sint32( particle_orientation::PERPENDICULAR ),        "Perpendicular" )
+		.value( "PS_PERPENDICULAR_COMMON", sint32( particle_orientation::PERPENDICULAR_COMMON ), "Perpendicular Common" )
+		;
+
+	db->create_type<nv::particle_origin>()
+		.value( "PS_CENTER",        sint32( particle_origin::CENTER ),        "Center" )
+		.value( "PS_TOP_LEFT",      sint32( particle_origin::TOP_LEFT ),      "Top left" )
+		.value( "PS_TOP_CENTER",    sint32( particle_origin::TOP_CENTER ),    "Top center" )
+		.value( "PS_TOP_RIGHT",     sint32( particle_origin::TOP_RIGHT ),     "Top right" )
+		.value( "PS_CENTER_LEFT",   sint32( particle_origin::CENTER_LEFT ),   "Center left" )
+		.value( "PS_CENTER_RIGHT",  sint32( particle_origin::CENTER_RIGHT ),  "Center right" )
+		.value( "PS_BOTTOM_LEFT",   sint32( particle_origin::BOTTOM_LEFT ),   "Bottom left" )
+		.value( "PS_BOTTOM_CENTER", sint32( particle_origin::BOTTOM_CENTER ), "Bottom center" )
+		.value( "PS_BOTTOM_RIGHT",  sint32( particle_origin::BOTTOM_RIGHT ),  "Bottom right" )
+		;
+
+	if ( m_debug_affector_types )
+	{
+		int you_could_get_rid_of_the_init_function_using_these; int error;
+
+		(*m_debug_affector_types)[ nv_particle_affector_linear_force ]
+			= db->create_type<nvpe_linear_force_data>( "linear force" )
+			.field( "force_vector", &nvpe_linear_force_data::force_vector )
+			.field( "average",      &nvpe_linear_force_data::average )
+			.get();
+
+		( *m_debug_affector_types )[nv_particle_affector_deflector_plane]
+			= db->create_type<nvpe_deflector_plane_data>( "deflector plane" )
+			.field( "plane_point",  &nvpe_deflector_plane_data::plane_point )
+			.field( "plane_normal", &nvpe_deflector_plane_data::plane_normal )
+			.field( "bounce",       &nvpe_deflector_plane_data::bounce )
+			.field( "distance",     &nvpe_deflector_plane_data::distance )
+			.get();
+
+		( *m_debug_affector_types )[nv_particle_affector_color_fader]
+			= db->create_type<nvpe_linear_force_data>( "color fader" )
+			.field( "adjustment", & nvpe_color_fader_data::adjustment )
+			.get();
+
+		( *m_debug_affector_types )[nv_particle_affector_scaler]
+			= db->create_type<nvpe_scaler_data>( "scaler" )
+			.field( "adjustment", & nvpe_scaler_data::adjustment )
+			.get();
+	}
+}
Index: trunk/src/engine/particle_manager.cc
===================================================================
--- trunk/src/engine/particle_manager.cc	(revision 518)
+++ trunk/src/engine/particle_manager.cc	(revision 518)
@@ -0,0 +1,195 @@
+#include "nv/engine/particle_manager.hh"
+
+#include "nv/lua/lua_raw.hh"
+#include "nv/lua/lua_math.hh"
+
+static int nv_psystem_emitter_closure( lua_State * L )
+{
+	luaL_checktype( L, 1, LUA_TTABLE );
+	lua_settop( L, 1 );
+	lua_pushvalue( L, lua_upvalueindex( 1 ) ); // type
+	lua_setfield( L, 1, "sub_type" );
+	lua_pushliteral( L, "emitter" );
+	lua_setfield( L, 1, "type" );
+	return 1;
+}
+
+static int nv_psystem_affector_closure( lua_State * L )
+{
+	luaL_checktype( L, 1, LUA_TTABLE );
+	lua_settop( L, 1 );
+	lua_pushvalue( L, lua_upvalueindex( 1 ) ); // type
+	lua_setfield( L, 1, "sub_type" );
+	lua_pushliteral( L, "affector" );
+	lua_setfield( L, 1, "type" );
+	return 1;
+}
+
+static int nv_psystem_emitter( lua_State * L )
+{
+	luaL_checkstring( L, 1 );
+	lua_settop( L, 1 );
+	lua_pushcclosure( L, nv_psystem_emitter_closure, 1 );
+	return 1;
+}
+
+static int nv_psystem_affector( lua_State * L )
+{
+	luaL_checkstring( L, 1 );
+	lua_settop( L, 1 );
+	lua_pushcclosure( L, nv_psystem_affector_closure, 1 );
+	return 1;
+}
+
+
+void nv::particle_manager::initialize( lua::state* state )
+{
+	lua_resource_manager_base::initialize( state );
+	lua_State* L = state->get_raw();
+ 	lua_pushcfunction( L, nv_psystem_emitter );
+ 	lua_setglobal( L, "emitter" );
+ 	lua_pushcfunction( L, nv_psystem_affector );
+ 	lua_setglobal( L, "affector" );
+}
+
+bool nv::particle_manager::load_resource( lua::table_guard& table, shash64 id )
+{
+	particle_system_data* data = new particle_system_data;
+	data->quota   = table.get<uint32>("quota", 1024 );
+//	data->local   = table.get<bool>("local_space", false );
+	data->accurate_facing = table.get<bool>("accurate_facing", false );
+	data->emitter_count   = 0;
+	data->affector_count  = 0;
+
+	const_string orientation = table.get_string( "orientation", "point" );
+	if ( orientation == "point" )                     { data->orientation = particle_orientation::POINT; }
+	else if ( orientation == "oriented" )             { data->orientation = particle_orientation::ORIENTED; }
+	else if ( orientation == "oriented_common" )      { data->orientation = particle_orientation::ORIENTED_COMMON; }
+	else if ( orientation == "perpendicular" )        { data->orientation = particle_orientation::PERPENDICULAR; }
+	else if ( orientation == "perpendicular_common" ) { data->orientation = particle_orientation::PERPENDICULAR_COMMON; }
+	else 
+	{
+		NV_LOG_ERROR( "Unknown orientation type! (", orientation, ")!" );
+		data->orientation = particle_orientation::POINT;
+	}
+
+	const_string origin = table.get_string( "origin", "center" );
+	if      ( origin == "center" )        { data->origin = particle_origin::CENTER; }
+	else if ( origin == "top_left" )      { data->origin = particle_origin::TOP_LEFT; }
+	else if ( origin == "top_center" )    { data->origin = particle_origin::TOP_CENTER; }
+	else if ( origin == "top_right" )     { data->origin = particle_origin::TOP_RIGHT; }
+	else if ( origin == "center_left" )   { data->origin = particle_origin::CENTER_LEFT; }
+	else if ( origin == "center_right" )  { data->origin = particle_origin::CENTER_RIGHT; }
+	else if ( origin == "bottom_left" )   { data->origin = particle_origin::BOTTOM_LEFT; }
+	else if ( origin == "bottom_center" ) { data->origin = particle_origin::BOTTOM_CENTER; }
+	else if ( origin == "bottom_right" )  { data->origin = particle_origin::BOTTOM_RIGHT; }
+	else 
+	{
+		NV_LOG_ERROR( "Unknown particle origin! (", origin, ")!" );
+		data->origin = particle_origin::CENTER;
+	}
+
+	data->common_up  = math::normalize( table.get<vec3>("common_up",  vec3(1,0,0) ) );
+	data->common_dir = math::normalize( table.get<vec3>("common_dir", vec3(0,1,0) ) );
+
+	vec2 def_size        = table.get<vec2>("size", vec2(0.1,0.1) );
+	uint32 elements = table.get_size();
+	for ( uint32 i = 0; i < elements; ++i )
+	{
+		lua::table_guard element( table, i+1 );
+		const_string type     = element.get_string( "type" );
+		const_string sub_type = element.get_string( "sub_type" );
+		if ( type == "emitter" )
+		{
+			if ( data->emitter_count < MAX_PARTICLE_EMITTERS )
+			{
+				particle_emitter_data& edata = data->emitters[ data->emitter_count ];
+				edata.emitter_func = particle_engine::get_emitter( sub_type );
+				if ( edata.emitter_func == nullptr )
+				{
+					NV_LOG_WARNING( "Unknown emitter type in particle system! (", sub_type, ")" );
+					break;
+				}
+
+				edata.position     = element.get<vec3>("position", vec3() );
+				edata.extents      = element.get<vec3>("extents", vec3(1,1,1) );
+				edata.extents[0]   = element.get<float>("width",  edata.extents[0] );
+				edata.extents[1]   = element.get<float>("depth",  edata.extents[1] );
+				edata.extents[2]   = element.get<float>("height", edata.extents[2] );
+				edata.extents[0]   = element.get<float>("radius",  edata.extents[0] );
+				edata.iextents     = element.get<vec3>("inner_extents", vec3() );
+				edata.iextents[0]  = element.get<float>("inner_width",  edata.iextents[0] );
+				edata.iextents[1]  = element.get<float>("inner_depth",  edata.iextents[1] );
+				edata.iextents[2]  = element.get<float>("inner_height", edata.iextents[2] );
+				edata.iextents[0]  = element.get<float>("inner_radius",  edata.iextents[0] );
+				edata.hextents     = 0.5f * edata.extents;
+				edata.ihextents    = 0.5f * edata.iextents;
+				edata.precise      = element.get<bool>("precise", false );
+				edata.square       = element.get<bool>("square", true );
+				vec4 color         = element.get<vec4>("color", vec4(1,1,1,1) );
+				edata.color_min    = element.get<vec4>("color_min", color );
+				edata.color_max    = element.get<vec4>("color_max", color );
+				vec2 size          = element.get<vec2>("size", def_size );
+				edata.size_min     = element.get<vec2>("size_min", size );
+				edata.size_max     = element.get<vec2>("size_max", size );
+				edata.angle        = element.get<float>("angle", 0.0f );
+				float velocity     = element.get<float>("velocity", 0.0f );
+				edata.velocity_min = element.get<float>("velocity_min", velocity );
+				edata.velocity_max = element.get<float>("velocity_max", velocity );
+				float lifetime     = element.get<float>("lifetime", 1.0f );
+				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 = 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   = 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 );
+				edata.dir          = math::normalize( element.get<vec3>("direction", vec3(0,1,0) ) );
+				
+				edata.odir = vec3( 0, 0, 1 );
+				if ( edata.dir != vec3( 0, 1, 0 ) && edata.dir != vec3( 0, -1, 0 ) )
+					edata.odir = math::normalize( math::cross( edata.dir, vec3( 0, 1, 0 ) ) );
+				edata.cdir = math::cross( edata.dir, edata.odir );
+
+				data->emitter_count++;
+			}
+			else
+			{
+				NV_LOG_ERROR( "Too many emitters (", MAX_PARTICLE_EMITTERS, " is MAX)!" );
+			}
+		}
+		else if ( type == "affector" )
+		{
+			if ( data->affector_count < MAX_PARTICLE_AFFECTORS )
+			{
+				particle_affector_data& adata = data->affectors[ data->affector_count ];
+				auto afuncs = particle_engine::get_affector( sub_type );
+				if ( !afuncs.init || !afuncs.process )
+				{
+					NV_LOG_WARNING( "Unknown affector type in particle system! (", sub_type, ")" );
+					continue;
+				}
+				adata.process = afuncs.process;
+				if ( !afuncs.init( &element, &adata ) )
+				{
+					NV_LOG_WARNING( "Bad data passed to ", sub_type, " affector in particle system!" );
+				}
+				data->affector_count++;
+			}
+			else
+			{
+				NV_LOG_ERROR( "Too many affectors (", MAX_PARTICLE_AFFECTORS, " is MAX)!" );
+			}
+		}
+		else 
+		{
+			NV_LOG_WARNING( "Unknown element in particle system! (", type, ")" );
+		}
+	}
+	add( id, data );
+	return true;
+}
+
Index: trunk/src/engine/renderer.cc
===================================================================
--- trunk/src/engine/renderer.cc	(revision 517)
+++ trunk/src/engine/renderer.cc	(revision 518)
@@ -121,4 +121,5 @@
 	m_context->set_viewport( ss.get_viewport() );
 
+
 	if ( pass.cstate.buffers != buffer_mask::NO_BUFFER )
 		m_context->clear( pass.cstate );
