Index: /trunk/nv/gfx/particle_engine.hh
===================================================================
--- /trunk/nv/gfx/particle_engine.hh	(revision 311)
+++ /trunk/nv/gfx/particle_engine.hh	(revision 312)
@@ -13,5 +13,20 @@
 namespace nv
 {
-	static const int MAX_PARTICLE_EMMITERS = 8;
+	static const int MAX_PARTICLE_EMMITERS  = 8;
+	static const int MAX_PARTICLE_AFFECTORS = 8;
+
+	struct particle_emmiter_data;
+	struct particle_affector_data;
+	struct particle;
+
+	typedef void (*particle_emmiter_func)( const particle_emmiter_data*, particle*, uint32 count );
+	typedef void (*particle_affector_func)( const particle_affector_data*, particle*, float factor, uint32 count );
+	typedef bool (*particle_affector_init_func)( lua::table_guard* table, particle_affector_data* data );
+
+	struct particle_affector_funcs
+	{
+		particle_affector_func      process;
+		particle_affector_init_func init;
+	};
 
 	enum class particle_orientation
@@ -35,17 +50,4 @@
 		BOTTOM_RIGHT,
 	};
-	enum class particle_emmiter_type
-	{
-		POINT,
-		BOX,
-		CYLINDER,
-		SPHERE,
-		CYLINDROID,
-		ELLIPSOID,
-		HOLLOW_CYLINDER,
-		HOLLOW_SPHERE,
-		HOLLOW_CYLINDROID,
-		HOLLOW_ELLIPSOID,
-	};
 
 	struct particle_vtx
@@ -65,6 +67,5 @@
 		vec3   position;
 		vec4   color;
-		vec3   dir;
-		float  velocity;
+		vec3   velocity;
 		vec2   size;
 		//float  rotation;
@@ -72,8 +73,13 @@
 	};
 
+	struct particle_affector_data
+	{
+		particle_affector_func process;
+		uint8 paramters[4*8*16];
+	};
 
 	struct particle_emmiter_data
 	{
-		particle_emmiter_type type;
+		particle_emmiter_func emmiter_func;
 		vec3   position;
 		vec3   extents;
@@ -111,5 +117,4 @@
 	struct particle_system_data
 	{
-		vec3   gravity;
 		uint32 quota;
 		bool   local;
@@ -117,8 +122,10 @@
 		vec3   common_dir;
 		bool   accurate_facing;
-		particle_orientation  orientation;
-		particle_origin       origin;
-		uint32                emmiter_count;
-		particle_emmiter_data emmiters[MAX_PARTICLE_EMMITERS]; 
+		particle_orientation   orientation;
+		particle_origin        origin;
+		uint32                 emmiter_count;
+		particle_emmiter_data  emmiters[MAX_PARTICLE_EMMITERS]; 
+		uint32                 affector_count;
+		particle_affector_data affectors[MAX_PARTICLE_AFFECTORS]; 
 	};
 
@@ -154,6 +161,10 @@
 		void update( particle_system system, const scene_state& s, uint32 ms );
 		void set_texcoords( particle_system system, vec2 a, vec2 b );
+		void register_emmiter_type( const std::string& name, particle_emmiter_func func );
+		void register_affector_type( const std::string& name, particle_affector_init_func init, particle_affector_func process );
 		~particle_engine();
 	private:
+		void register_standard_emmiters();
+		void register_standard_affectors();
 		void generate_data( particle_system_info* info );
 		void destroy_particles( particle_system_info* info, uint32 ms );
@@ -162,18 +173,20 @@
 		void update_emmiters( particle_system_info* info, uint32 ms );
 
-		device*                        m_device;
-		context*                       m_context;
+		device*  m_device;
+		context* m_context;
 
-		program                        m_program_local;
-		program                        m_program_world;
+		program  m_program_local;
+		program  m_program_world;
 
-		mat4                           m_view_matrix;
-		mat4                           m_model_matrix;
-		vec3                           m_inv_view_dir;
-		vec3                           m_camera_pos;
+		mat4     m_view_matrix;
+		mat4     m_model_matrix;
+		vec3     m_inv_view_dir;
+		vec3     m_camera_pos;
 
-		entity_store< particle_system_info, particle_system > m_systems;
-		std::unordered_map< std::string, uint32 >             m_names;
-		std::vector< particle_system_data >                   m_data; // constant
+		entity_store< particle_system_info, particle_system >      m_systems;
+		std::unordered_map< std::string, uint32 >                  m_names;
+		std::vector< particle_system_data >                        m_data; 
+		std::unordered_map< std::string, particle_emmiter_func >   m_emmiters;
+		std::unordered_map< std::string, particle_affector_funcs > m_affectors;
 	};
 
Index: /trunk/nv/math.hh
===================================================================
--- /trunk/nv/math.hh	(revision 311)
+++ /trunk/nv/math.hh	(revision 312)
@@ -235,4 +235,16 @@
 	}
 
+	template <typename T>
+	glm::detail::tvec3<T> normalize_safe( 
+		const glm::detail::tvec3<T>& x, 
+		const glm::detail::tvec3<T>& def = vec3()
+	)
+	{
+		typename glm::detail::tvec3<T>::value_type sqr = x.x * x.x + x.y * x.y + x.z * x.z;
+		return ( sqr > 0 ? x * glm::inversesqrt(sqr) : def );
+	}
+
+
+
 } // namespace nv
 
Index: /trunk/src/gfx/particle_engine.cc
===================================================================
--- /trunk/src/gfx/particle_engine.cc	(revision 311)
+++ /trunk/src/gfx/particle_engine.cc	(revision 312)
@@ -47,4 +47,251 @@
 	"}\n";
 
+using namespace nv;
+
+static void nv_particle_emmiter_point( const particle_emmiter_data*, particle* p, uint32 count )
+{
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		p[i].position = vec3();
+	}
+
+}
+
+static void nv_particle_emmiter_box( const particle_emmiter_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_emmiter_cylinder( const particle_emmiter_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] );
+		p[i].position = 
+			rellipse.x * pe->cdir +
+			r.frange( 0.0f, pe->extents[1] ) * pe->dir +
+			rellipse.y * pe->odir;
+	}
+}
+
+static void nv_particle_emmiter_sphere( const particle_emmiter_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];
+		p[i].position = 
+			rsphere.x * pe->cdir +
+			rsphere.y * pe->dir +
+			rsphere.z * pe->odir;
+	}
+}
+
+static void nv_particle_emmiter_cylindroid( const particle_emmiter_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 );
+		p[i].position = 
+			rellipse.x * pe->cdir +
+			r.frange( 0.0f, pe->extents[1] ) * pe->dir +
+			rellipse.y * pe->odir;
+	}
+}
+
+static void nv_particle_emmiter_ellipsoid( const particle_emmiter_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 );
+		p[i].position = 
+			rsphere.x * pe->cdir +
+			rsphere.y * pe->dir +
+			rsphere.z * pe->odir;
+	}
+}
+
+static void nv_particle_emmiter_hollow_cylinder( const particle_emmiter_data* pe, particle* p, uint32 count )
+{
+	random& r = random::get();
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		vec2 rellipse = r.hollow_disk_point( 
+			pe->ihextents[0],
+			pe->hextents[0],
+			pe->precise );
+		p[i].position = 
+			rellipse.x * pe->cdir +
+			r.frange( 0.0f, pe->extents[1] ) * pe->dir +
+			rellipse.y * pe->odir;
+	}
+}
+
+static void nv_particle_emmiter_hollow_sphere( const particle_emmiter_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 );
+		p[i].position = 
+			rellipse.x * pe->cdir +
+			rellipse.y * pe->dir +
+			rellipse.z * pe->odir;
+	}
+}
+
+static void nv_particle_emmiter_hollow_cylindroid( const particle_emmiter_data* pe, particle* p, uint32 count )
+{
+	random& r = random::get();
+	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] ), 
+			pe->precise );
+		p[i].position = 
+			rellipse.x * pe->cdir +
+			r.frange( 0.0f, pe->extents[1] ) * pe->dir +
+			rellipse.y * pe->odir;
+	}
+}
+
+static void nv_particle_emmiter_hollow_ellipsoid( const particle_emmiter_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 );
+		p[i].position = 
+			rellipse.x * pe->cdir +
+			rellipse.y * pe->dir +
+			rellipse.z * pe->odir;
+	}
+}
+
+struct nvpe_linear_force_data
+{
+	nv::vec3 force_vector;
+	bool     average;
+};
+
+static bool nv_particle_affector_linear_force_init( lua::table_guard* table, particle_affector_data* data )
+{
+	nvpe_linear_force_data* datap = ((nvpe_linear_force_data*)data->paramters);
+	datap->force_vector = table->get<vec3>("force_vector", vec3() );
+	datap->average      = table->get<bool>("average", false );
+	return true;
+}
+
+static void nv_particle_affector_linear_force( const particle_affector_data* data, particle* p, float factor, uint32 count )
+{
+	nvpe_linear_force_data* datap = ((nvpe_linear_force_data*)data->paramters);
+	if ( datap->average )
+	{
+		float norm_factor = glm::min( factor, 1.0f );
+		for ( uint32 i = 0; i < count; ++i ) 
+			p[i].velocity = datap->force_vector * norm_factor + p[i].velocity * ( 1.0f - norm_factor );
+	}
+	else
+	{
+		vec3 scvector = datap->force_vector * factor;
+		for ( uint32 i = 0; i < count; ++i ) p[i].velocity += scvector;
+	}
+}
+
+struct nvpe_deflector_plane_data
+{
+	nv::vec3 plane_point;
+	nv::vec3 plane_normal;
+	float    bounce;
+	float    distance;
+};
+
+static bool nv_particle_affector_deflector_plane_init( lua::table_guard* table, particle_affector_data* data )
+{
+	nvpe_deflector_plane_data* datap = ((nvpe_deflector_plane_data*)data->paramters);
+	datap->plane_point  = table->get<vec3>("plane_point",  vec3() );
+	datap->plane_normal = table->get<vec3>("plane_normal", vec3(0.0f,1.0f,0.0f) );
+	datap->plane_normal = normalize_safe( datap->plane_normal, vec3(0.0f,1.0f,0.0f) );
+	datap->bounce       = table->get<float>("bounce", 0.0f );
+	datap->distance     = -glm::dot( datap->plane_normal, datap->plane_point ) / glm::sqrt(glm::dot( datap->plane_normal, datap->plane_normal ) );
+	return true;
+}
+
+static void nv_particle_affector_deflector_plane( const particle_affector_data* data, particle* p, float factor, uint32 count )
+{
+	nvpe_deflector_plane_data* datap = ((nvpe_deflector_plane_data*)data->paramters);
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		particle& pt = p[i];
+		vec3 direction  = pt.velocity * factor;
+		if ( glm::dot( datap->plane_normal, pt.position + direction ) + datap->distance <= 0.0f )
+		{
+			float val = glm::dot( datap->plane_normal, pt.position ) + datap->distance;
+			if ( val > 0.0f )
+			{
+				vec3 part_dir = direction * ( -val / glm::dot( datap->plane_normal, direction ) );
+				pt.position = pt.position + part_dir + ( part_dir - direction ) * datap->bounce;
+				pt.velocity = glm::reflect( pt.velocity, datap->plane_normal ) * datap->bounce;
+			}
+		}
+	}
+}
+
+struct nvpe_color_fader_data
+{
+	nv::vec4 adjustment;
+};
+
+static bool nv_particle_affector_color_fader_init( lua::table_guard* table, particle_affector_data* data )
+{
+	nvpe_color_fader_data* datap = ((nvpe_color_fader_data*)data->paramters);
+	datap->adjustment = table->get<vec4>("adjustment",  vec4() );
+	return true;
+}
+
+static void nv_particle_affector_color_fader( const particle_affector_data* data, particle* p, float factor, uint32 count )
+{
+	nvpe_color_fader_data* datap = ((nvpe_color_fader_data*)data->paramters);
+	vec4 adjustment = datap->adjustment * factor;
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		p[i].color = glm::clamp( p[i].color + adjustment, 0.0f, 1.0f );
+	}
+}
+
+struct nvpe_scaler_data
+{
+	nv::vec2 adjustment;
+};
+
+static bool nv_particle_affector_scaler_init( lua::table_guard* table, particle_affector_data* data )
+{
+	nvpe_scaler_data* datap = ((nvpe_scaler_data*)data->paramters);
+	float rate        = table->get<float>("rate", 0.0f );
+	datap->adjustment = table->get<vec2>("adjustment",  vec2(rate,rate) );
+	return true;
+}
+
+static void nv_particle_affector_scaler( const particle_affector_data* data, particle* p, float factor, uint32 count )
+{
+	nvpe_scaler_data* datap = ((nvpe_scaler_data*)data->paramters);
+	vec2 adjustment = datap->adjustment * factor;
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		p[i].size = glm::max( p[i].size + adjustment, vec2() );
+	}
+}
+
 void nv::particle_engine::load( lua::table_guard& table )
 {
@@ -60,9 +307,9 @@
 	auto& data = m_data.back();
 
-	data.gravity = table.get<vec3>("gravity", vec3() );
 	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;
 
 	std::string orientation = table.get_string( "orientation", "point" );
@@ -109,18 +356,12 @@
 			{
 				particle_emmiter_data& edata = data.emmiters[ data.emmiter_count ];
-
-				     if ( sub_type == "point" )             edata.type = particle_emmiter_type::POINT;
-				else if ( sub_type == "box" )               edata.type = particle_emmiter_type::BOX;
-				else if ( sub_type == "cylinder" )          edata.type = particle_emmiter_type::CYLINDER;
-				else if ( sub_type == "sphere" )            edata.type = particle_emmiter_type::SPHERE;
-				else if ( sub_type == "cylindroid" )        edata.type = particle_emmiter_type::CYLINDROID;
-				else if ( sub_type == "ellipsoid" )         edata.type = particle_emmiter_type::ELLIPSOID;
-				else if ( sub_type == "hollow_cylinder" )   edata.type = particle_emmiter_type::HOLLOW_CYLINDER;
-				else if ( sub_type == "hollow_sphere" )     edata.type = particle_emmiter_type::HOLLOW_SPHERE;
-				else if ( sub_type == "hollow_cylindroid" ) edata.type = particle_emmiter_type::HOLLOW_CYLINDROID;
-				else if ( sub_type == "hollow_ellipsoid" )  edata.type = particle_emmiter_type::HOLLOW_ELLIPSOID;
+				auto emmiter_iter = m_emmiters.find( sub_type );
+				if ( emmiter_iter != m_emmiters.end() )
+				{
+					edata.emmiter_func = emmiter_iter->second;
+				}
 				else
 				{
-					edata.type = particle_emmiter_type::POINT;
+					edata.emmiter_func = nv_particle_emmiter_point;
 					NV_LOG( LOG_WARNING, "Unknown emmiter type in particle system! (" << sub_type << ")" );
 				}
@@ -177,5 +418,28 @@
 		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( LOG_WARNING, "Bad data passed to " << sub_type << " affector in particle system!" );
+					}
+				}
+				else
+				{
+					data.affector_count--;
+					NV_LOG( LOG_WARNING, "Unknown affector type in particle system! (" << sub_type << ")" );
+				}
+			}
+			else
+			{
+				NV_LOG( LOG_ERROR, "Too many affectors (" << MAX_PARTICLE_AFFECTORS << " is MAX)!" );
+			}
 		}
 		else 
@@ -193,4 +457,7 @@
 	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 );
+
+	register_standard_emmiters();
+	register_standard_affectors();
 }
 
@@ -324,12 +591,11 @@
 	vec3 z( 0.0f, 0.0f ,1.0f );
 
-	mat3 rot_mat;
-	vec3 right;
-	mat3 irot_mat = glm::transpose( mat3( m_view_matrix ) );
 	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 );
 
 	for ( uint32 i = 0; i < info->count; ++i )
@@ -346,9 +612,9 @@
 			right   = glm::normalize( glm::cross( view_dir, vec3( 0, 1, 0 ) ) );
 			rot_mat = mat3( right, glm::cross( right, -view_dir ), -view_dir );
-//			rot_mat = irot_mat * glm::mat3_cast( glm::angleAxis( pdata.rotation, z ) );
 			break;
 		case particle_orientation::ORIENTED :
-			right   = glm::normalize( glm::cross( pdata.dir, view_dir ) );
-			rot_mat = mat3( right, pdata.dir, glm::cross( pdata.dir, right ) );
+			pdir    = normalize_safe( pdata.velocity, pdir );
+			right   = glm::normalize( glm::cross( pdir, view_dir ) );
+			rot_mat = mat3( right, pdir, glm::cross( pdir, right ) );
 			break;
 		case particle_orientation::ORIENTED_COMMON :
@@ -357,5 +623,6 @@
 			break;
 		case particle_orientation::PERPENDICULAR :
-			right   = glm::normalize( glm::cross( common_up, pdata.dir ) );
+			pdir    = normalize_safe( pdata.velocity, pdir );
+			right   = glm::normalize( glm::cross( common_up, pdir ) );
 			rot_mat = mat3( right, common_up, glm::cross( common_up, right ) );
 			break;
@@ -412,9 +679,11 @@
 	vec3 source;
 	mat3 orient;
-	if ( !info->data->local ) 
+	bool local = info->data->local;
+	if ( !local ) 
 	{
 		source = vec3( m_model_matrix[3] );
 		orient = mat3( m_model_matrix );
 	}
+
 	float fms = float(ms);
 
@@ -431,109 +700,7 @@
 				{
 					particle& pinfo = info->particles[info->count];
-					switch ( edata.type )
-					{
-					case particle_emmiter_type::POINT:
-						pinfo.position = source;
-						break;
-					case particle_emmiter_type::BOX:
-						pinfo.position = source + orient * ( 
-							r.frange( -edata.hextents[0], edata.hextents[0] ) * edata.cdir +
-							r.frange( 0.0f, edata.extents[1] ) * edata.dir +
-							r.frange( -edata.hextents[2], edata.hextents[2] ) * edata.odir
-						);
-						break;
-					case particle_emmiter_type::CYLINDER:
-						{
-							vec2 rellipse = r.disk_point( edata.precise );
-							pinfo.position = source + orient * ( 
-								rellipse.x * edata.extents[0] * edata.cdir +
-								r.frange( 0.0f, edata.extents[1] ) * edata.dir +
-								rellipse.y * edata.extents[0] * edata.odir
-								);
-						}
-						break;
-					case particle_emmiter_type::SPHERE:
-						{
-							vec3 rellipse = r.sphere_point( edata.precise );
-							pinfo.position = source + orient * ( 
-								rellipse.x * edata.extents[0] * edata.cdir +
-								rellipse.y * edata.extents[0] * edata.dir +
-								rellipse.z * edata.extents[0] * edata.odir
-								);
-						}
-						break;
-					// TODO : this method is NOT uniform if width != height!
-					case particle_emmiter_type::CYLINDROID:
-						{
-							vec2 rellipse = r.ellipse_point( vec2( edata.hextents[0], edata.hextents[2] ), edata.precise );
-							pinfo.position = source + orient * ( 
-								rellipse.x * edata.cdir +
-								r.frange( 0.0f, edata.extents[1] ) * edata.dir +
-								rellipse.y * edata.odir
-							);
-						}
-						break;
-					// TODO : this method is NOT uniform if all dimension are not equal!
-					case particle_emmiter_type::ELLIPSOID:
-						{
-							vec3 rsphere = r.ellipsoid_point( edata.hextents, edata.precise );
- 							pinfo.position = source + orient * ( 
-								rsphere.x * edata.cdir +
-								rsphere.y * edata.dir +
-								rsphere.z * edata.odir
-								);
-						}
-						break;
-					case particle_emmiter_type::HOLLOW_CYLINDER:
-						{
-							vec2 rellipse = r.hollow_disk_point( 
-								edata.ihextents[0],
-								edata.hextents[0],
-								edata.precise );
-							pinfo.position = source + orient * ( 
-								rellipse.x * edata.cdir +
-								r.frange( 0.0f, edata.extents[1] ) * edata.dir +
-								rellipse.y * edata.odir
-								);
-						}
-						break;
-					case particle_emmiter_type::HOLLOW_SPHERE:
-						{
-							vec3 rellipse = r.hollow_sphere_point( 
-								edata.ihextents[0],
-								edata.hextents[0],
-								edata.precise );
-							pinfo.position = source + orient * ( 
-								rellipse.x * edata.cdir +
-								rellipse.y * edata.dir +
-								rellipse.z * edata.odir
-								);
-						}
-						break;
-					case particle_emmiter_type::HOLLOW_CYLINDROID:
-						{
-							vec2 rellipse = r.hollow_ellipse_point( 
-								vec2( edata.ihextents[0], edata.ihextents[2] ), 
-								vec2( edata.hextents[0], edata.hextents[2] ), 
-								edata.precise );
-							pinfo.position = source + orient * ( 
-								rellipse.x * edata.cdir +
-								r.frange( 0.0f, edata.extents[1] ) * edata.dir +
-								rellipse.y * edata.odir
-								);
-						}
-						break;
-					// TODO : this method is NOT uniform if all dimension are not equal!
-					case particle_emmiter_type::HOLLOW_ELLIPSOID:
-						{
-							vec3 rellipse = r.hollow_ellipsoid_point( edata.ihextents, edata.hextents, edata.precise );
-							pinfo.position = source + orient * ( 
-								rellipse.x * edata.cdir +
-								rellipse.y * edata.dir +
-								rellipse.z * edata.odir
-								);
-						}
-						break;
-					}
+					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 ? 
@@ -542,5 +709,5 @@
 						edata.size_min : r.range( edata.size_min, edata.size_max );
 					if ( edata.square ) pinfo.size.y = pinfo.size.x;
-					pinfo.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 ?
@@ -548,5 +715,5 @@
 					//pinfo.rotation = r.frand( 360.0f );
 
-					pinfo.dir = edata.dir;
+					pinfo.velocity = edata.dir;
 					if ( edata.angle > 0.0f )
 					{
@@ -555,9 +722,11 @@
 						float sin_theta = glm::sqrt(1.0f - cos_theta * cos_theta );
 						float phi       = r.frange( 0.0f, 2*glm::pi<float>() );
-						pinfo.dir = orient * 
+						pinfo.velocity  = orient * 
 							( edata.odir * ( glm::cos(phi) * sin_theta ) +
 							edata.cdir * ( glm::sin(phi)*sin_theta ) + 
 							edata.dir  * cos_theta );
 					}
+
+					pinfo.velocity *= velocity;
 
 					info->count++;
@@ -569,32 +738,24 @@
 }
 
-void nv::particle_engine::update_particles( particle_system_info* system, uint32 ms )
-{
-	uint32 ticks = ms - system->last_update;
-	if ( ticks > 0 )
-		for ( uint32 i = 0; i < system->count; ++i )
-		{
-			particle& pdata = system->particles[i];
-			vec3 velocity_vec = pdata.dir * pdata.velocity;
-			pdata.position += velocity_vec * ( 0.001f * ticks );
-			velocity_vec   += system->data->gravity * ( 0.001f * ticks );
-			pdata.velocity  = glm::length( velocity_vec );
-			if ( pdata.velocity > 0.0f ) pdata.dir       = glm::normalize( velocity_vec );
-			if ( pdata.position.y < 0.0f )
-			{
-				if ( pdata.velocity > 1.0f )
-				{
-					pdata.position.y = -pdata.position.y;
-					pdata.velocity   = 0.5f*pdata.velocity;
-					pdata.dir.y      = -pdata.dir.y;
-				}
-				else
-				{
-					pdata.position.y = 0.0f;
-					pdata.velocity   = 0.0f;
-				}
-			}
-		}
-		system->last_update = ms;
+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;
+
+	uint32 acount = info->data->affector_count;
+	for ( uint32 i = 0; i < acount; ++i )
+	{
+		const particle_affector_data* padata = &(info->data->affectors[i]);
+		padata->process( padata, info->particles, factor, info->count );
+	}
+
+
+	for ( uint32 i = 0; i < info->count; ++i )
+	{
+		particle& pdata = info->particles[i];
+		pdata.position += pdata.velocity * factor;
+	}
+	info->last_update = ms;
 }
 
@@ -630,2 +791,36 @@
 
 }
+
+void nv::particle_engine::register_emmiter_type( const std::string& 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_affector_type( const std::string& name, particle_affector_init_func init, particle_affector_func process )
+{
+	m_affectors[ name ].init    = init;
+	m_affectors[ name ].process = process;
+}
+
+void nv::particle_engine::register_standard_affectors()
+{
+	register_affector_type( "linear_force",    nv_particle_affector_linear_force_init, nv_particle_affector_linear_force );
+	register_affector_type( "deflector_plane", nv_particle_affector_deflector_plane_init, nv_particle_affector_deflector_plane );
+	register_affector_type( "color_fader",     nv_particle_affector_color_fader_init, nv_particle_affector_color_fader );
+	register_affector_type( "scaler",          nv_particle_affector_scaler_init, nv_particle_affector_scaler );
+}
+
