Index: trunk/nv.lua
===================================================================
--- trunk/nv.lua	(revision 319)
+++ trunk/nv.lua	(revision 320)
@@ -1,2 +1,118 @@
+project "nv-core"
+	location (_ACTION)
+	language "C++"
+	kind "StaticLib"
+	includedirs { "." }
+	files { "nv/core/**.hh", "nv/interface/**.hh", "nv/detail/**.inc", "src/core/**.cc" }
+	targetname "nv-core"
+
+project "nv-lib"
+	location (_ACTION)
+	language "C++"
+	kind "StaticLib"
+	includedirs { "." }
+	files { "nv/lib/**.hh", "nv/lib/**.inl", "nv/lib/**.inc", "src/lib/**.cc" }
+	links { "nv-core" }
+	targetname "nv-lib"
+
+project "nv-curses"
+	location (_ACTION)
+	language "C++"
+	kind "StaticLib"
+	includedirs { "." }
+	files { "nv/curses/**.hh",  "src/curses/**.cc" }
+	links { "nv-core", "nv-lib" }
+	targetname "nv-curses"
+
+project "nv-fmod"
+	location (_ACTION)
+	language "C++"
+	kind "StaticLib"
+	includedirs { "." }
+	files { "nv/fmod/**.hh",    "src/fmod/**.cc" }
+	links { "nv-core", "nv-lib" }
+	targetname "nv-fmod"
+
+project "nv-sdl"
+	location (_ACTION)
+	language "C++"
+	kind "StaticLib"
+	includedirs { "." }
+	files { "nv/sdl/**.hh",     "src/sdl/**.cc" }
+	links { "nv-core", "nv-lib" }
+	targetname "nv-sdl"
+
+project "nv-gl"
+	location (_ACTION)
+	language "C++"
+	kind "StaticLib"
+	includedirs { "." }
+	files { "nv/gl/**.hh",     "src/gl/**.cc" }
+	links { "nv-core", "nv-lib" }
+	targetname "nv-gl"
+
+project "nv-lua"
+	location (_ACTION)
+	language "C++"
+	kind "StaticLib"
+	includedirs { "." }
+	files { "nv/lua/**.hh",     "src/lua/**.cc" }
+	links { "nv-core", "nv-lib" }
+	targetname "nv-lua"
+
+project "nv-rogue"
+	location (_ACTION)
+	language "C++"
+	kind "StaticLib"
+	includedirs { "." }
+	files { "nv/rogue/**.hh",     "src/rogue/**.cc" }
+	links { "nv-core" }
+	targetname "nv-rogue"
+
+project "nv-io"
+	location (_ACTION)
+	language "C++"
+	kind "StaticLib"
+	includedirs { "." }
+	files { "nv/io/**.hh",     "src/io/**.cc" }
+	links { "nv-core" }
+	targetname "nv-io"
+
+project "nv-gfx"
+	location (_ACTION)
+	language "C++"
+	kind "StaticLib"
+	includedirs { "." }
+	files { "nv/gfx/**.hh",  "src/gfx/**.cc" }
+	links { "nv-core" }
+	targetname "nv-gfx"
+
+project "nv-engine"
+	location (_ACTION)
+	language "C++"
+	kind "StaticLib"
+	includedirs { "." }
+	files { "nv/engine/**.hh",  "src/engine/**.cc" }
+	links { "nv-core", "nv-lua" }
+	targetname "nv-engine"
+
+project "nv-formats"
+	location (_ACTION)
+	language "C++"
+	kind "StaticLib"
+	includedirs { "." }
+	files { "nv/formats/**.hh", "src/formats/**.cc" }
+	links { "nv-core", "nv-lib", "nv-io", "nv-gfx" }
+	targetname "nv-formats"
+
+project "nv-gui"
+	location (_ACTION)
+	language "C++"
+	kind "StaticLib"
+	includedirs { "." }
+	files { "nv/gui/**.hh",     "src/gui/**.cc" }
+	links { "nv-core", "nv-io", "nv-gfx" }
+	targetname "nv-gui"
+
 project "nv"
 	location (_ACTION)
@@ -4,5 +120,5 @@
 	kind "StaticLib"
 	includedirs { "." }
-	files { "nv/**.hh", "nv/**.inl", "nv/**.inc", "src/**.cc" }
+	links { "nv-core", "nv-lib", "nv-curses", "nv-sdl", "nv-fmod", "nv-lua", "nv-gl", "nv-rogue", "nv-io", "nv-gfx", "nv-engine", "nv-formats", "nv-gui" }
 	targetname "nv"
 
Index: trunk/nv/engine/particle_engine.hh
===================================================================
--- trunk/nv/engine/particle_engine.hh	(revision 320)
+++ trunk/nv/engine/particle_engine.hh	(revision 320)
@@ -0,0 +1,201 @@
+// Copyright (C) 2014 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+#ifndef NV_GFX_PARTICLE_ENGINE
+#define NV_GFX_PARTICLE_ENGINE
+
+#include <nv/core/common.hh>
+#include <nv/core/math.hh>
+#include <nv/core/array.hh>
+#include <nv/core/handle.hh>
+#include <nv/lua/lua_state.hh>
+#include <nv/gfx/texture_atlas.hh>
+#include <nv/interface/scene_node.hh>
+#include <nv/interface/context.hh>
+
+namespace nv
+{
+	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
+	{
+		POINT,
+		ORIENTED,
+		ORIENTED_COMMON,
+		PERPENDICULAR,
+		PERPENDICULAR_COMMON,
+	};
+	enum class particle_origin
+	{
+		CENTER,
+		TOP_LEFT,
+		TOP_CENTER,
+		TOP_RIGHT,
+		CENTER_LEFT,
+		CENTER_RIGHT,
+		BOTTOM_LEFT,
+		BOTTOM_CENTER,
+		BOTTOM_RIGHT,
+	};
+
+	struct particle_vtx
+	{
+		vec3 position;
+		vec2 texcoord;
+		vec4 color;
+	};
+
+	struct particle_quad
+	{
+		particle_vtx data[6];
+	};
+
+	struct particle
+	{
+		vec3   position;
+		vec4   color;
+		vec3   velocity;
+		vec2   size;
+		//float  rotation;
+		uint32 death;
+	};
+
+	struct particle_affector_data
+	{
+		particle_affector_func process;
+		uint8 paramters[4*8*16];
+	};
+
+	struct particle_emmiter_data
+	{
+		particle_emmiter_func emmiter_func;
+		vec3   position;
+		vec3   extents;
+		vec3   hextents;
+		vec3   iextents;
+		vec3   ihextents;
+		vec4   color_min;
+		vec4   color_max;
+		vec2   size_min;
+		vec2   size_max;
+		bool   precise;
+		bool   square;
+		float  angle;
+		float  velocity_min;
+		float  velocity_max;
+		uint32 lifetime_min;
+		uint32 lifetime_max;
+		vec3   dir;
+		vec3   odir;
+		vec3   cdir;
+		uint32 duration_min;
+		uint32 duration_max;
+		uint32 repeat_min;
+		uint32 repeat_max;
+		float  rate;
+	};
+
+	struct particle_emmiter_info
+	{
+		bool   active;
+		uint32 next_toggle;
+		float  last_create;
+	};
+
+	struct particle_system_data
+	{
+		uint32 quota;
+		bool   local;
+		vec3   common_up;
+		vec3   common_dir;
+		bool   accurate_facing;
+		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]; 
+	};
+
+
+	struct particle_system_info
+	{
+		bool test;
+		uint32                last_update;
+		particle*             particles;
+		particle_quad*        quads;
+		particle_emmiter_info emmiters[MAX_PARTICLE_EMMITERS];
+
+		uint32                count;
+		vertex_array          vtx_array;
+		buffer                vtx_buffer;
+		//		bool                           own_va;
+		//		uint32                         offset;
+
+		const particle_system_data*    data;
+	};
+
+	struct particle_system_tag {};
+	typedef handle< uint32, 16, 16, particle_system_tag > particle_system;
+
+	class particle_engine
+	{
+	public:
+		particle_engine( context* a_context );
+		void load( lua::table_guard& table );
+		void draw( particle_system system, const render_state& rs, const scene_state& ss );
+		particle_system create_system( const std::string& id );
+		void release( particle_system system );
+		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 );
+		void create_particles( particle_system_info* info, uint32 ms );
+		void update_particles( particle_system_info* info, uint32 ms );
+		void update_emmiters( particle_system_info* info, uint32 ms );
+
+		device*  m_device;
+		context* m_context;
+
+		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;
+
+		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;
+	};
+
+}
+
+#endif // NV_GFX_PARTICLE_ENGINE
Index: trunk/nv/gfx/particle_engine.hh
===================================================================
--- trunk/nv/gfx/particle_engine.hh	(revision 319)
+++ 	(revision )
@@ -1,201 +1,0 @@
-// Copyright (C) 2014 ChaosForge Ltd
-// http://chaosforge.org/
-//
-// This file is part of NV Libraries.
-// For conditions of distribution and use, see copyright notice in nv.hh
-
-#ifndef NV_GFX_PARTICLE_ENGINE
-#define NV_GFX_PARTICLE_ENGINE
-
-#include <nv/core/common.hh>
-#include <nv/core/math.hh>
-#include <nv/core/array.hh>
-#include <nv/core/handle.hh>
-#include <nv/lua/lua_state.hh>
-#include <nv/gfx/texture_atlas.hh>
-#include <nv/interface/scene_node.hh>
-#include <nv/interface/context.hh>
-
-namespace nv
-{
-	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
-	{
-		POINT,
-		ORIENTED,
-		ORIENTED_COMMON,
-		PERPENDICULAR,
-		PERPENDICULAR_COMMON,
-	};
-	enum class particle_origin
-	{
-		CENTER,
-		TOP_LEFT,
-		TOP_CENTER,
-		TOP_RIGHT,
-		CENTER_LEFT,
-		CENTER_RIGHT,
-		BOTTOM_LEFT,
-		BOTTOM_CENTER,
-		BOTTOM_RIGHT,
-	};
-
-	struct particle_vtx
-	{
-		vec3 position;
-		vec2 texcoord;
-		vec4 color;
-	};
-
-	struct particle_quad
-	{
-		particle_vtx data[6];
-	};
-
-	struct particle
-	{
-		vec3   position;
-		vec4   color;
-		vec3   velocity;
-		vec2   size;
-		//float  rotation;
-		uint32 death;
-	};
-
-	struct particle_affector_data
-	{
-		particle_affector_func process;
-		uint8 paramters[4*8*16];
-	};
-
-	struct particle_emmiter_data
-	{
-		particle_emmiter_func emmiter_func;
-		vec3   position;
-		vec3   extents;
-		vec3   hextents;
-		vec3   iextents;
-		vec3   ihextents;
-		vec4   color_min;
-		vec4   color_max;
-		vec2   size_min;
-		vec2   size_max;
-		bool   precise;
-		bool   square;
-		float  angle;
-		float  velocity_min;
-		float  velocity_max;
-		uint32 lifetime_min;
-		uint32 lifetime_max;
-		vec3   dir;
-		vec3   odir;
-		vec3   cdir;
-		uint32 duration_min;
-		uint32 duration_max;
-		uint32 repeat_min;
-		uint32 repeat_max;
-		float  rate;
-	};
-
-	struct particle_emmiter_info
-	{
-		bool   active;
-		uint32 next_toggle;
-		float  last_create;
-	};
-
-	struct particle_system_data
-	{
-		uint32 quota;
-		bool   local;
-		vec3   common_up;
-		vec3   common_dir;
-		bool   accurate_facing;
-		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]; 
-	};
-
-
-	struct particle_system_info
-	{
-		bool test;
-		uint32                last_update;
-		particle*             particles;
-		particle_quad*        quads;
-		particle_emmiter_info emmiters[MAX_PARTICLE_EMMITERS];
-
-		uint32                count;
-		vertex_array          vtx_array;
-		buffer                vtx_buffer;
-		//		bool                           own_va;
-		//		uint32                         offset;
-
-		const particle_system_data*    data;
-	};
-
-	struct particle_system_tag {};
-	typedef handle< uint32, 16, 16, particle_system_tag > particle_system;
-
-	class particle_engine
-	{
-	public:
-		particle_engine( context* a_context );
-		void load( lua::table_guard& table );
-		void draw( particle_system system, const render_state& rs, const scene_state& ss );
-		particle_system create_system( const std::string& id );
-		void release( particle_system system );
-		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 );
-		void create_particles( particle_system_info* info, uint32 ms );
-		void update_particles( particle_system_info* info, uint32 ms );
-		void update_emmiters( particle_system_info* info, uint32 ms );
-
-		device*  m_device;
-		context* m_context;
-
-		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;
-
-		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;
-	};
-
-}
-
-#endif // NV_GFX_PARTICLE_ENGINE
Index: trunk/src/engine/particle_engine.cc
===================================================================
--- trunk/src/engine/particle_engine.cc	(revision 320)
+++ trunk/src/engine/particle_engine.cc	(revision 320)
@@ -0,0 +1,830 @@
+// Copyright (C) 2014 ChaosForge Ltd
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+#include "nv/engine/particle_engine.hh"
+
+#include <nv/interface/device.hh>
+#include <nv/core/random.hh>
+#include <nv/lua/lua_glm.hh>
+#include <nv/core/logging.hh>
+#include <cmath>
+
+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"
+	"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"
+	"}\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"
+	"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"
+	"}\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"
+	"void main(void)\n"
+	"{\n"
+	"	vec4 tex_color = texture2D( nv_t_diffuse, v_texcoord );\n"
+	"	gl_FragColor   = v_color * tex_color;\n"
+	"}\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 )
+{
+	std::string id = table.get_string( "id" );
+	if ( id == "" )
+	{
+		NV_LOG( 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;
+
+	std::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( LOG_ERROR, "Unknown orientation type! (" << orientation << ")!" );
+		data.orientation = particle_orientation::POINT;
+	}
+
+	std::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( LOG_ERROR, "Unknown particle origin! (" << origin << ")!" );
+		data.origin = particle_origin::CENTER;
+	}
+
+	data.common_up  = glm::normalize( table.get<vec3>("common_up",  vec3(1,0,0) ) );
+	data.common_dir = glm::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 );
+		std::string type     = element.get_string("type");
+		std::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( 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 = uint32( element.get<float>("lifetime_min", lifetime ) * 1000.f );
+				edata.lifetime_max = uint32( element.get<float>("lifetime_max", lifetime ) * 1000.f );
+				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 );
+				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.rate         = element.get<float>("rate", 1.0f );
+				edata.dir          = glm::normalize( element.get<vec3>("direction", vec3(0,1,0) ) );
+				
+				edata.odir = glm::vec3( 0, 0, 1 );
+				if ( edata.dir != vec3( 0, 1, 0 ) && edata.dir != vec3( 0, -1, 0 ) )
+					edata.odir = glm::normalize( glm::cross( edata.dir, vec3( 0, 1, 0 ) ) );		edata.cdir = glm::cross( edata.dir, edata.odir );
+
+				data.emmiter_count++;
+			}
+			else
+			{
+				NV_LOG( 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( 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 
+		{
+			NV_LOG( LOG_WARNING, "Unknown element in particle system! (" << type << ")" );
+		}
+	}
+
+}
+
+nv::particle_engine::particle_engine( context* a_context )
+{
+	m_context       = a_context;
+	m_device        = a_context->get_device();
+	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();
+}
+
+nv::particle_system nv::particle_engine::create_system( const std::string& id )
+{
+	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->data     = data;
+	uint32 ecount = data->emmiter_count;
+	for ( uint32 i = 0; i < ecount; ++i )
+	{
+		info->emmiters[i].active      = true;
+		info->emmiters[i].last_create = 0;
+		info->emmiters[i].next_toggle = random::get().urange( data->emmiters[i].duration_min, data->emmiters[i].duration_max );
+	}
+
+	info->count = 0;
+	info->particles = new particle[ data->quota ];
+	info->quads     = new particle_quad[ data->quota ];
+	info->vtx_array = m_context->create_vertex_array<particle_vtx>( 
+		(particle_vtx*)info->quads, data->quota*6, STREAM_DRAW );
+	info->vtx_buffer = m_context->find_buffer( info->vtx_array, slot::POSITION );
+	info->last_update = 0;
+	info->test = false;
+//	result->m_own_va      = true;
+//	result->m_offset      = 0;
+
+	return result;
+}
+
+void nv::particle_engine::draw( particle_system system, const render_state& rs, const scene_state& ss )
+{
+	particle_system_info* info = m_systems.get( system );
+	if ( info )
+	{
+		m_context->draw( nv::TRIANGLES, rs, ss, info->data->local ?  m_program_local : m_program_world, info->vtx_array, info->count * 6 );
+	}
+}
+
+nv::particle_engine::~particle_engine()
+{
+	m_device->release( m_program_world );
+	m_device->release( m_program_local );
+}
+
+void nv::particle_engine::release( particle_system system )
+{
+	particle_system_info* info = m_systems.get( system );
+	if ( info )
+	{
+		delete[] info->particles;
+		delete[] info->quads;
+		//if ( system->own_va ) 
+		m_context->release( info->vtx_array );
+		m_systems.destroy( system );
+	}
+}
+
+void nv::particle_engine::update( particle_system system, const scene_state& s, uint32 ms )
+{
+	particle_system_info* info = m_systems.get( system );
+	if ( info )
+	{
+		m_view_matrix  = s.get_view();
+		m_model_matrix = s.get_model();
+		m_camera_pos   = s.get_camera().get_position();
+		m_inv_view_dir = glm::normalize(-s.get_camera().get_direction());
+
+		update_emmiters( info, ms );
+		destroy_particles( info, ms );
+		create_particles( info, ms );
+		update_particles( info, ms );
+
+		generate_data( info );
+		m_context->update( info->vtx_buffer, info->quads, /*system->m_offset*sizeof(particle_quad)*/ 0, info->count*sizeof(particle_quad) );
+	}
+}
+
+void nv::particle_engine::set_texcoords( particle_system system, vec2 a, vec2 b )
+{
+	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 < info->data->quota; ++i )
+		{
+			particle_quad& rdata   = info->quads[i];
+			rdata.data[0].texcoord = texcoords[0];
+			rdata.data[1].texcoord = texcoords[1];
+			rdata.data[2].texcoord = texcoords[2];
+			rdata.data[3].texcoord = texcoords[3];
+			rdata.data[4].texcoord = texcoords[2];
+			rdata.data[5].texcoord = texcoords[1];
+		}
+	}
+}
+
+void nv::particle_engine::generate_data( particle_system_info* info )
+{
+	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;  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 );
+
+	for ( uint32 i = 0; i < info->count; ++i )
+	{
+		const particle& pdata = info->particles[i];
+		particle_quad& rdata  = info->quads[i];
+
+		vec3 view_dir( m_inv_view_dir );
+		if ( accurate_facing ) view_dir = glm::normalize( m_camera_pos - pdata.position );
+
+		switch ( orientation )
+		{
+		case particle_orientation::POINT :
+			right   = glm::normalize( glm::cross( view_dir, vec3( 0, 1, 0 ) ) );
+			rot_mat = mat3( right, glm::cross( right, -view_dir ), -view_dir );
+			break;
+		case particle_orientation::ORIENTED :
+			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 :
+			right   = glm::normalize( glm::cross( common_dir, view_dir ) );
+			rot_mat = mat3( right, common_dir, glm::cross( common_dir, right ) );
+			break;
+		case particle_orientation::PERPENDICULAR :
+			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;
+		case particle_orientation::PERPENDICULAR_COMMON :
+			right   = glm::normalize( glm::cross( common_up, common_dir ) );
+			rot_mat = mat3( right, common_up, glm::cross( common_up, right ) );
+			break;
+		}
+
+		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[1].position = pdata.position + s1;
+		rdata.data[1].color    = pdata.color;
+
+		rdata.data[2].position = pdata.position + s2;
+		rdata.data[2].color    = pdata.color;
+
+		rdata.data[3].position = pdata.position + s3;
+		rdata.data[3].color    = pdata.color;
+
+		rdata.data[4] = rdata.data[2];
+		rdata.data[5] = rdata.data[1];
+	}
+}
+
+void nv::particle_engine::destroy_particles( particle_system_info* info, uint32 ms )
+{
+	if ( info->count > 0 )
+		for ( sint32 i = info->count-1; i >= 0; --i )
+		{
+			particle& pinfo = info->particles[i];
+			if ( //pdata.position.y < 0.0f ||
+				ms >= pinfo.death )
+			{
+				info->count--;
+				std::swap( info->particles[i], info->particles[info->count] );
+			}
+		}
+}
+
+void nv::particle_engine::create_particles( particle_system_info* info, uint32 ms )
+{
+	uint32 ecount = info->data->emmiter_count;
+	if ( ecount == 0 ) return;
+
+	random& r = random::get();
+	vec3 source;
+	mat3 orient;
+	bool local = info->data->local;
+	if ( !local ) 
+	{
+		source = vec3( m_model_matrix[3] );
+		orient = mat3( m_model_matrix );
+	}
+
+	float fms = float(ms);
+
+	for ( uint32 i = 0; i < ecount; ++i )
+	{
+		const auto& edata = info->data->emmiters[i];
+		auto& einfo = info->emmiters[i];
+		if ( einfo.active )
+		{
+			float period = 1000.f / edata.rate;
+			while ( fms - einfo.last_create > period )
+			{
+				if ( info->count < info->data->quota-1 )
+				{
+					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 ? 
+						edata.color_min : r.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 );
+					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 );
+					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.velocity = edata.dir;
+					if ( edata.angle > 0.0f )
+					{
+						float emission_angle = glm::radians( edata.angle );
+						float cos_theta = r.frange( cos( emission_angle ), 1.0f );
+						float sin_theta = glm::sqrt(1.0f - cos_theta * cos_theta );
+						float phi       = r.frange( 0.0f, 2*glm::pi<float>() );
+						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++;
+				}
+				einfo.last_create += period;
+			}
+		}
+	}
+}
+
+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;
+}
+
+void nv::particle_engine::update_emmiters( particle_system_info* info, uint32 ms )
+{
+	uint32 ecount = info->data->emmiter_count;
+	if ( ecount == 0 ) return;
+	random& r = random::get();
+
+	for ( uint32 i = 0; i < ecount; ++i )
+	{
+		const auto& edata = info->data->emmiters[i];
+		auto& einfo = info->emmiters[i];
+
+		if ( einfo.next_toggle != 0 && ms >= einfo.next_toggle )
+		{
+			if ( einfo.active )
+			{
+				einfo.active = false;
+				if ( edata.repeat_min > 0 )
+					einfo.next_toggle += r.urange( edata.repeat_min, edata.repeat_max );
+				else
+					einfo.next_toggle = 0;
+			}
+			else
+			{
+				einfo.active = true;
+				einfo.last_create = float( einfo.next_toggle );
+				einfo.next_toggle += r.urange( edata.duration_min, edata.duration_max );
+			}
+		}
+	}
+
+}
+
+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 );
+}
+
Index: trunk/src/gfx/particle_engine.cc
===================================================================
--- trunk/src/gfx/particle_engine.cc	(revision 319)
+++ 	(revision )
@@ -1,830 +1,0 @@
-// Copyright (C) 2014 ChaosForge Ltd
-// This file is part of NV Libraries.
-// For conditions of distribution and use, see copyright notice in nv.hh
-
-#include "nv/gfx/particle_engine.hh"
-
-#include <nv/interface/device.hh>
-#include <nv/core/random.hh>
-#include <nv/lua/lua_glm.hh>
-#include <nv/core/logging.hh>
-#include <cmath>
-
-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"
-	"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"
-	"}\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"
-	"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"
-	"}\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"
-	"void main(void)\n"
-	"{\n"
-	"	vec4 tex_color = texture2D( nv_t_diffuse, v_texcoord );\n"
-	"	gl_FragColor   = v_color * tex_color;\n"
-	"}\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 )
-{
-	std::string id = table.get_string( "id" );
-	if ( id == "" )
-	{
-		NV_LOG( 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;
-
-	std::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( LOG_ERROR, "Unknown orientation type! (" << orientation << ")!" );
-		data.orientation = particle_orientation::POINT;
-	}
-
-	std::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( LOG_ERROR, "Unknown particle origin! (" << origin << ")!" );
-		data.origin = particle_origin::CENTER;
-	}
-
-	data.common_up  = glm::normalize( table.get<vec3>("common_up",  vec3(1,0,0) ) );
-	data.common_dir = glm::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 );
-		std::string type     = element.get_string("type");
-		std::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( 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 = uint32( element.get<float>("lifetime_min", lifetime ) * 1000.f );
-				edata.lifetime_max = uint32( element.get<float>("lifetime_max", lifetime ) * 1000.f );
-				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 );
-				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.rate         = element.get<float>("rate", 1.0f );
-				edata.dir          = glm::normalize( element.get<vec3>("direction", vec3(0,1,0) ) );
-				
-				edata.odir = glm::vec3( 0, 0, 1 );
-				if ( edata.dir != vec3( 0, 1, 0 ) && edata.dir != vec3( 0, -1, 0 ) )
-					edata.odir = glm::normalize( glm::cross( edata.dir, vec3( 0, 1, 0 ) ) );		edata.cdir = glm::cross( edata.dir, edata.odir );
-
-				data.emmiter_count++;
-			}
-			else
-			{
-				NV_LOG( 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( 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 
-		{
-			NV_LOG( LOG_WARNING, "Unknown element in particle system! (" << type << ")" );
-		}
-	}
-
-}
-
-nv::particle_engine::particle_engine( context* a_context )
-{
-	m_context       = a_context;
-	m_device        = a_context->get_device();
-	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();
-}
-
-nv::particle_system nv::particle_engine::create_system( const std::string& id )
-{
-	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->data     = data;
-	uint32 ecount = data->emmiter_count;
-	for ( uint32 i = 0; i < ecount; ++i )
-	{
-		info->emmiters[i].active      = true;
-		info->emmiters[i].last_create = 0;
-		info->emmiters[i].next_toggle = random::get().urange( data->emmiters[i].duration_min, data->emmiters[i].duration_max );
-	}
-
-	info->count = 0;
-	info->particles = new particle[ data->quota ];
-	info->quads     = new particle_quad[ data->quota ];
-	info->vtx_array = m_context->create_vertex_array<particle_vtx>( 
-		(particle_vtx*)info->quads, data->quota*6, STREAM_DRAW );
-	info->vtx_buffer = m_context->find_buffer( info->vtx_array, slot::POSITION );
-	info->last_update = 0;
-	info->test = false;
-//	result->m_own_va      = true;
-//	result->m_offset      = 0;
-
-	return result;
-}
-
-void nv::particle_engine::draw( particle_system system, const render_state& rs, const scene_state& ss )
-{
-	particle_system_info* info = m_systems.get( system );
-	if ( info )
-	{
-		m_context->draw( nv::TRIANGLES, rs, ss, info->data->local ?  m_program_local : m_program_world, info->vtx_array, info->count * 6 );
-	}
-}
-
-nv::particle_engine::~particle_engine()
-{
-	m_device->release( m_program_world );
-	m_device->release( m_program_local );
-}
-
-void nv::particle_engine::release( particle_system system )
-{
-	particle_system_info* info = m_systems.get( system );
-	if ( info )
-	{
-		delete[] info->particles;
-		delete[] info->quads;
-		//if ( system->own_va ) 
-		m_context->release( info->vtx_array );
-		m_systems.destroy( system );
-	}
-}
-
-void nv::particle_engine::update( particle_system system, const scene_state& s, uint32 ms )
-{
-	particle_system_info* info = m_systems.get( system );
-	if ( info )
-	{
-		m_view_matrix  = s.get_view();
-		m_model_matrix = s.get_model();
-		m_camera_pos   = s.get_camera().get_position();
-		m_inv_view_dir = glm::normalize(-s.get_camera().get_direction());
-
-		update_emmiters( info, ms );
-		destroy_particles( info, ms );
-		create_particles( info, ms );
-		update_particles( info, ms );
-
-		generate_data( info );
-		m_context->update( info->vtx_buffer, info->quads, /*system->m_offset*sizeof(particle_quad)*/ 0, info->count*sizeof(particle_quad) );
-	}
-}
-
-void nv::particle_engine::set_texcoords( particle_system system, vec2 a, vec2 b )
-{
-	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 < info->data->quota; ++i )
-		{
-			particle_quad& rdata   = info->quads[i];
-			rdata.data[0].texcoord = texcoords[0];
-			rdata.data[1].texcoord = texcoords[1];
-			rdata.data[2].texcoord = texcoords[2];
-			rdata.data[3].texcoord = texcoords[3];
-			rdata.data[4].texcoord = texcoords[2];
-			rdata.data[5].texcoord = texcoords[1];
-		}
-	}
-}
-
-void nv::particle_engine::generate_data( particle_system_info* info )
-{
-	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;  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 );
-
-	for ( uint32 i = 0; i < info->count; ++i )
-	{
-		const particle& pdata = info->particles[i];
-		particle_quad& rdata  = info->quads[i];
-
-		vec3 view_dir( m_inv_view_dir );
-		if ( accurate_facing ) view_dir = glm::normalize( m_camera_pos - pdata.position );
-
-		switch ( orientation )
-		{
-		case particle_orientation::POINT :
-			right   = glm::normalize( glm::cross( view_dir, vec3( 0, 1, 0 ) ) );
-			rot_mat = mat3( right, glm::cross( right, -view_dir ), -view_dir );
-			break;
-		case particle_orientation::ORIENTED :
-			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 :
-			right   = glm::normalize( glm::cross( common_dir, view_dir ) );
-			rot_mat = mat3( right, common_dir, glm::cross( common_dir, right ) );
-			break;
-		case particle_orientation::PERPENDICULAR :
-			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;
-		case particle_orientation::PERPENDICULAR_COMMON :
-			right   = glm::normalize( glm::cross( common_up, common_dir ) );
-			rot_mat = mat3( right, common_up, glm::cross( common_up, right ) );
-			break;
-		}
-
-		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[1].position = pdata.position + s1;
-		rdata.data[1].color    = pdata.color;
-
-		rdata.data[2].position = pdata.position + s2;
-		rdata.data[2].color    = pdata.color;
-
-		rdata.data[3].position = pdata.position + s3;
-		rdata.data[3].color    = pdata.color;
-
-		rdata.data[4] = rdata.data[2];
-		rdata.data[5] = rdata.data[1];
-	}
-}
-
-void nv::particle_engine::destroy_particles( particle_system_info* info, uint32 ms )
-{
-	if ( info->count > 0 )
-		for ( sint32 i = info->count-1; i >= 0; --i )
-		{
-			particle& pinfo = info->particles[i];
-			if ( //pdata.position.y < 0.0f ||
-				ms >= pinfo.death )
-			{
-				info->count--;
-				std::swap( info->particles[i], info->particles[info->count] );
-			}
-		}
-}
-
-void nv::particle_engine::create_particles( particle_system_info* info, uint32 ms )
-{
-	uint32 ecount = info->data->emmiter_count;
-	if ( ecount == 0 ) return;
-
-	random& r = random::get();
-	vec3 source;
-	mat3 orient;
-	bool local = info->data->local;
-	if ( !local ) 
-	{
-		source = vec3( m_model_matrix[3] );
-		orient = mat3( m_model_matrix );
-	}
-
-	float fms = float(ms);
-
-	for ( uint32 i = 0; i < ecount; ++i )
-	{
-		const auto& edata = info->data->emmiters[i];
-		auto& einfo = info->emmiters[i];
-		if ( einfo.active )
-		{
-			float period = 1000.f / edata.rate;
-			while ( fms - einfo.last_create > period )
-			{
-				if ( info->count < info->data->quota-1 )
-				{
-					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 ? 
-						edata.color_min : r.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 );
-					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 );
-					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.velocity = edata.dir;
-					if ( edata.angle > 0.0f )
-					{
-						float emission_angle = glm::radians( edata.angle );
-						float cos_theta = r.frange( cos( emission_angle ), 1.0f );
-						float sin_theta = glm::sqrt(1.0f - cos_theta * cos_theta );
-						float phi       = r.frange( 0.0f, 2*glm::pi<float>() );
-						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++;
-				}
-				einfo.last_create += period;
-			}
-		}
-	}
-}
-
-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;
-}
-
-void nv::particle_engine::update_emmiters( particle_system_info* info, uint32 ms )
-{
-	uint32 ecount = info->data->emmiter_count;
-	if ( ecount == 0 ) return;
-	random& r = random::get();
-
-	for ( uint32 i = 0; i < ecount; ++i )
-	{
-		const auto& edata = info->data->emmiters[i];
-		auto& einfo = info->emmiters[i];
-
-		if ( einfo.next_toggle != 0 && ms >= einfo.next_toggle )
-		{
-			if ( einfo.active )
-			{
-				einfo.active = false;
-				if ( edata.repeat_min > 0 )
-					einfo.next_toggle += r.urange( edata.repeat_min, edata.repeat_max );
-				else
-					einfo.next_toggle = 0;
-			}
-			else
-			{
-				einfo.active = true;
-				einfo.last_create = float( einfo.next_toggle );
-				einfo.next_toggle += r.urange( edata.duration_min, edata.duration_max );
-			}
-		}
-	}
-
-}
-
-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 );
-}
-
