Index: /trunk/nv/core/random.hh
===================================================================
--- /trunk/nv/core/random.hh	(revision 508)
+++ /trunk/nv/core/random.hh	(revision 509)
@@ -10,8 +10,36 @@
 #include <nv/common.hh>
 #include <nv/stl/math.hh>
+#include <nv/stl/limits.hh>
 #include <nv/stl/type_traits/primary.hh>
 
 namespace nv
 {
+	enum class random_dist
+	{
+		LINEAR,
+		GAUSSIAN,
+		RGAUSSIAN,
+		STEP_1,
+		STEP_2,
+		STEP_3,
+		STEP_4,
+		MLINEAR,
+		MGAUSSIAN,
+		MRGAUSSIAN,
+		MSTEP_1,
+		MSTEP_2,
+		MSTEP_3,
+		MSTEP_4,
+	};
+
+
+	template< typename T >
+	struct random_range
+	{
+		T min;
+		T max;
+		random_dist dist = random_dist::LINEAR;
+	};
+
 
 	class random_base
@@ -42,4 +70,9 @@
 		}
 
+		bool coin_flip()
+		{
+			return rand() % 2 == 0;
+		}
+
 		f32 frand()
 		{
@@ -54,5 +87,5 @@
 		inline sint32 srange( sint32 min, sint32 max )
 		{
-			NV_ASSERT( max >= min, "Bad srange argument!" );
+			if ( max < min ) ::nv::swap( max, min );
 			// this method probably reduces range //
 			uint32 roll = urand( static_cast<uint32>( max - min ) + 1 );
@@ -62,5 +95,5 @@
 		inline uint32 urange( uint32 min, uint32 max )
 		{
-			NV_ASSERT( max >= min, "Bad srange argument!" );
+			if ( max < min ) ::nv::swap( max, min );
 			return urand( max - min + 1 ) + min;
 		}
@@ -68,5 +101,5 @@
 		inline f32 frange( f32 min, f32 max )
 		{
-			NV_ASSERT( max >= min, "Bad srange argument!" );
+			if ( max < min ) ::nv::swap( max, min );
 			return frand( max - min ) + min;
 		}
@@ -84,32 +117,44 @@
 
 		template < typename T >
-		math::tvec2<T> range( math::tvec2<T> min, math::tvec2<T> max )
-		{
-			return math::tvec2<T>(
-				range_impl( min.x, max.x, is_floating_point<T>() ),
-				range_impl( min.y, max.y, is_floating_point<T>() )
-				);
-		}
-
-		template < typename T >
-		math::tvec3<T> range( math::tvec3<T> min, math::tvec3<T> max )
-		{
-			return math::tvec3<T>(
-				range_impl( min.x, max.x, is_floating_point<T>() ),
-				range_impl( min.y, max.y, is_floating_point<T>() ),
-				range_impl( min.z, max.z, is_floating_point<T>() )
-				);
-		}
-
-		template < typename T >
-		math::tvec4<T> range( math::tvec4<T> min, math::tvec4<T> max )
-		{
-			return math::tvec4<T>(
-				range_impl( min.x, max.x, is_floating_point<T>() ),
-				range_impl( min.y, max.y, is_floating_point<T>() ),
-				range_impl( min.z, max.z, is_floating_point<T>() ),
-				range_impl( min.w, max.w, is_floating_point<T>() )
-				);
-		}
+		T range( T min, T max )
+		{
+			return component_wise<T>::apply( min, max, range_op( this ) );
+
+// 			return component_wise<T>::apply( min, max, [&]( auto a, auto b )
+// 			{
+// 				return range_impl( a, b );
+// 			} );
+		}
+
+
+// 		template < typename T >
+// 		math::tvec2<T> range( math::tvec2<T> min, math::tvec2<T> max )
+// 		{
+// 			return math::tvec2<T>(
+// 				range_impl( min.x, max.x, is_floating_point<T>() ),
+// 				range_impl( min.y, max.y, is_floating_point<T>() )
+// 				);
+// 		}
+// 
+// 		template < typename T >
+// 		math::tvec3<T> range( math::tvec3<T> min, math::tvec3<T> max )
+// 		{
+// 			return math::tvec3<T>(
+// 				range_impl( min.x, max.x, is_floating_point<T>() ),
+// 				range_impl( min.y, max.y, is_floating_point<T>() ),
+// 				range_impl( min.z, max.z, is_floating_point<T>() )
+// 				);
+// 		}
+// 
+// 		template < typename T >
+// 		math::tvec4<T> range( math::tvec4<T> min, math::tvec4<T> max )
+// 		{
+// 			return math::tvec4<T>(
+// 				range_impl( min.x, max.x, is_floating_point<T>() ),
+// 				range_impl( min.y, max.y, is_floating_point<T>() ),
+// 				range_impl( min.z, max.z, is_floating_point<T>() ),
+// 				range_impl( min.w, max.w, is_floating_point<T>() )
+// 				);
+// 		}
 
 
@@ -209,18 +254,179 @@
 		vec3 precise_hollow_ellipsoid_point( const vec3& iradii, const vec3& oradii );
 
+		// Box Muller
+		template < typename T = f32 >
+		T gaussian_bm()
+		{
+			T u1;
+			T u2;
+			do { u1 = frand(); u2 = frand(); } while ( u1 <= numeric_limits<T>::min() );
+			T z0 = sqrt( T(-2) * log( u1 ) ) * cos( math::two_pi<T>() * u2 );
+			return z0;
+		}
+
+		// Marsaglia
+		template < typename T = f32 >
+		T gaussian_m()
+		{
+			T x1, x2, w, y1;
+			do
+			{
+				x1 = T( 2 ) * frand() - T( 1 );
+				x2 = T( 2 ) * frand() - T( 1 );
+				w = x1 * x1 + x2 * x2;
+			} while ( w >= T( 1 ) );
+			w = sqrt( ( T(-2) * log( w ) ) / w );
+			y1 = x1 * w;
+			return y1;
+		}
+
+		template < typename T = f32 >
+		T gaussian_01( T sigma = T( 0.1 ) )
+		{
+			T g = T( 0.5 ) + gaussian_m<T>() * sigma * T(0.5);
+			return math::clamp( g, T( 0 ), T( 1 ) );
+		}
+
+		template < typename T = f32 >
+		T rgaussian_01( T sigma = T( 0.1 ) )
+		{
+			T g = gaussian_01<T>( sigma ) - T(0.5);
+			if ( g < T(0) ) g += T(1);
+			return math::clamp( g, T( 0 ), T( 1 ) );
+		}
+
+		template < typename T >
+		T eval( const random_range<T>& r )
+		{
+			switch ( r.dist )
+			{
+			case random_dist::LINEAR     : return component_wise<T>::apply( r.min, r.max, range_op( this ) );
+			case random_dist::GAUSSIAN   : return component_wise<T>::apply( r.min, r.max, gaussian_op( this ) );
+			case random_dist::RGAUSSIAN  : return component_wise<T>::apply( r.min, r.max, rgaussian_op( this ) );
+			case random_dist::STEP_1     : return component_wise<T>::apply( r.min, r.max, coin_flip_op( this ) );
+			case random_dist::STEP_2     : return component_wise<T>::apply( r.min, r.max, step_op( this, 2 ) );
+			case random_dist::STEP_3     : return component_wise<T>::apply( r.min, r.max, step_op( this, 3 ) );
+			case random_dist::STEP_4     : return component_wise<T>::apply( r.min, r.max, step_op( this, 4 ) );
+			case random_dist::MLINEAR    : return frand()                        * ( r.max - r.min ) + r.min;
+			case random_dist::MGAUSSIAN  : return gaussian_01<float>()           * ( r.max - r.min ) + r.min;
+			case random_dist::MRGAUSSIAN : return rgaussian_01<float>()          * ( r.max - r.min ) + r.min;
+			case random_dist::MSTEP_1    : return coin_flip() ? r.min : r.max;
+			case random_dist::MSTEP_2    : return fstep( r.min, r.max, 2 );
+			case random_dist::MSTEP_3    : return fstep( r.min, r.max, 3 );
+			case random_dist::MSTEP_4    : return fstep( r.min, r.max, 4 );
+			default: return T();
+			}
+		}
+
 	protected:
 		static seed_type randomized_seed();
 
-		template <typename T>
-		T range_impl( T min, T max, const true_type& )
-		{
-			return frange( min, max );
-		}
-
-		template <typename T>
-		T range_impl( T min, T max, const false_type& )
-		{
-			return srange( min, max );
-		}
+		struct range_op
+		{
+			range_op( random_base* base ) : self( base ) {}
+			random_base* self;
+			template <typename T, typename enable_if< is_floating_point<T>::value >::type* = nullptr>
+			T operator()( T a, T b ) { return self->frange( a, b );  }
+			template <typename T, typename enable_if< !is_floating_point<T>::value >::type* = nullptr>
+			T operator()( T a, T b ) { return self->srange( a, b ); }
+		};
+
+		struct coin_flip_op
+		{
+			coin_flip_op( random_base* base ) : self( base ) {}
+			random_base* self;
+			template <typename T>
+			T operator()( T a, T b ) { return self->coin_flip() ? a : b; }
+		};
+
+		struct step_op
+		{
+			step_op( random_base* base, uint32 s ) : self( base ), steps(s) {}
+			uint32 steps;
+			random_base* self;
+			template <typename T>
+			T operator()( T a, T b ) { return self->fstep( a, b, steps ); }
+		};
+
+		struct gaussian_op
+		{
+			gaussian_op( random_base* base ) : self( base ) {}
+			random_base* self;
+			template <typename T>
+			T operator()( T a, T b ) { return self->gaussian_01<T>() * ( b - a ) + a; }
+		};
+
+		struct rgaussian_op
+		{
+			rgaussian_op( random_base* base ) : self( base ) {}
+			random_base* self;
+			template <typename T>
+			T operator()( T a, T b ) { return ( self->rgaussian_01<T>() ) * ( b - a ) + a; }
+		};
+
+		template <typename T, typename enable_if< !math::is_vec<T>::value >::type* = nullptr >
+		T fstep( T min, T max, uint32 steps )
+		{
+			return T( srand( steps + 1 ) ) * ( max - min ) / T( steps ) + min; 
+		}
+
+		template <typename T, typename enable_if< math::is_vec<T>::value >::type* = nullptr >
+		T fstep( T min, T max, uint32 steps )
+		{
+			return value_type_t<T>( srand( steps + 1 ) ) * ( max - min ) / value_type_t<T>( steps ) + min;
+		}
+
+		template < typename T >
+		struct component_wise
+		{
+			template < typename Functor >
+			static T apply( T min, T max, typename enable_if< is_arithmetic<T>::value, Functor >::type f )
+			{
+				return f( min, max );
+			}
+		};
+
+		template < typename T > 
+		struct component_wise< math::tvec2< T > >
+		{
+			template < typename Functor >
+			static math::tvec2<T> apply( math::tvec2<T> min, math::tvec2<T> max, Functor f )
+			{
+				return math::tvec2<T>(
+					f( min.x, max.x ),
+					f( min.y, max.y )
+					);
+			}
+		};
+
+		template < typename T >
+		struct component_wise< math::tvec3< T > >
+		{
+			template < typename Functor >
+			static math::tvec3<T> apply( math::tvec3<T> min, math::tvec3<T> max, Functor f )
+			{
+				return math::tvec3<T>(
+					f( min.x, max.x ),
+					f( min.y, max.y ),
+					f( min.z, max.z )
+					);
+			}
+		};
+
+		template < typename T >
+		struct component_wise< math::tvec4< T > >
+		{
+			template < typename Functor >
+			static math::tvec4<T> apply( math::tvec4<T> min, math::tvec4<T> max, Functor f )
+			{
+				return math::tvec4<T>(
+					f( min.x, max.x ),
+					f( min.y, max.y ),
+					f( min.z, max.z ),
+					f( min.w, max.w )
+					);
+			}
+		};
+
 	};
 
@@ -251,5 +457,12 @@
 	public:
 		explicit random_xor128( seed_type seed = randomized_seed() );
-		virtual seed_type set_seed( seed_type seed = 0 );
+		random_xor128( const random_xor128& other )
+		{
+			m_state[0] = other.m_state[0];
+			m_state[1] = other.m_state[1];
+			m_state[2] = other.m_state[2];
+			m_state[3] = other.m_state[3];
+		}
+		virtual seed_type set_seed( seed_type seed = randomized_seed() );
 		virtual result_type rand();
 	private:
Index: /trunk/nv/core/resource.hh
===================================================================
--- /trunk/nv/core/resource.hh	(revision 508)
+++ /trunk/nv/core/resource.hh	(revision 509)
@@ -45,4 +45,6 @@
 		virtual bool exists( resource_id id ) = 0;
 		virtual bool load_resource( const string_view& ) { return false; };
+		virtual void remove( resource_id id ) = 0;
+		virtual void rename( resource_id id, resource_id new_id ) = 0;
 
 		template< typename T >
@@ -247,4 +249,28 @@
 		}
 
+		template < typename T >
+		void remove( shash64 id )
+		{
+			auto m = m_handlers.find( resource_type_id( rtti_type_hash<T>::value ) );
+			NV_ASSERT( m != m_handlers.end(), "Resource type unrecognized!" );
+			m->second->remove( id );
+		}
+
+		template < typename T >
+		void rename( shash64 id, shash64 new_id )
+		{
+			auto m = m_handlers.find( resource_type_id( rtti_type_hash<T>::value ) );
+			NV_ASSERT( m != m_handlers.end(), "Resource type unrecognized!" );
+			m->second->rename( id, new_id );
+		}
+
+		template < typename T >
+		bool exists( shash64 id )
+		{
+			auto m = m_handlers.find( resource_type_id( rtti_type_hash<T>::value ) );
+			NV_ASSERT( m != m_handlers.end(), "Resource type unrecognized!" );
+			return m->second->exists( id );
+		}
+
 		virtual ~resource_manager()
 		{
Index: /trunk/nv/engine/animation.hh
===================================================================
--- /trunk/nv/engine/animation.hh	(revision 508)
+++ /trunk/nv/engine/animation.hh	(revision 509)
@@ -76,4 +76,5 @@
 		vector< animator_layer_data > layers;
 		pose_data_set*                poses;
+		string64                      id;
 	};
 
Index: /trunk/nv/engine/default_resource_manager.hh
===================================================================
--- /trunk/nv/engine/default_resource_manager.hh	(revision 508)
+++ /trunk/nv/engine/default_resource_manager.hh	(revision 509)
@@ -32,5 +32,5 @@
 	{
 	public:
-		explicit default_resource_manager( context* context );
+		explicit default_resource_manager( context* context, bool clear_material_paths = true );
 
 		void initialize( lua::state* lua );
@@ -46,4 +46,9 @@
 		}
 
+		mesh_source get_source( resource< data_channel_set > mesh )
+		{
+			return m_mesh_datas->get_source( mesh );
+		}
+
 		nv::string_view resolve( nv::shash64 h )
 		{
@@ -52,5 +57,5 @@
 
 		void reload_data();
-	private:
+	protected:
 		lua::state*            m_lua;
 		image_manager*         m_images;
Index: /trunk/nv/engine/material_manager.hh
===================================================================
--- /trunk/nv/engine/material_manager.hh	(revision 508)
+++ /trunk/nv/engine/material_manager.hh	(revision 509)
@@ -27,4 +27,5 @@
 	struct material
 	{
+		string128 id;
 		string128 paths[ 8 ];
 	};
@@ -43,7 +44,9 @@
 		virtual string_view get_storage_name() const { return "materials"; }
 		virtual string_view get_resource_name() const { return "material"; }
+		explicit material_manager( bool clear_paths ) : m_clear_paths( clear_paths ) {}
 	protected:
 		virtual bool load_resource( lua::table_guard& table, shash64 id );
 	private:
+		bool m_clear_paths;
 	};
 
Index: /trunk/nv/engine/mesh_manager.hh
===================================================================
--- /trunk/nv/engine/mesh_manager.hh	(revision 508)
+++ /trunk/nv/engine/mesh_manager.hh	(revision 509)
@@ -39,4 +39,5 @@
 	struct mesh_data
 	{
+		string128                              path;
 		vector< data_node_info >               infos;
 		vector< resource< data_channel_set > > meshes;
@@ -65,4 +66,10 @@
 	};
 
+	struct mesh_source
+	{
+		resource< mesh_data > mesh;
+		uint32                index;
+	};
+
 	class mesh_data_manager : public file_resource_manager< mesh_data >
 	{
@@ -74,10 +81,18 @@
 			resource< mesh_data > default = resource< mesh_data >(),
 			data_node_info* info = nullptr );
+
+		mesh_source get_source( resource< data_channel_set > mesh )
+		{
+			auto it = m_source_map.find( mesh.id() );
+			if ( it != m_source_map.end() ) return it->second;
+			return mesh_source{};
+		}
+
 		~mesh_data_manager() { delete m_strings; }
 	protected:
 		virtual bool load_resource( const string_view& id );
-
-		mesh_manager*           m_mesh_manager;
-		string_table*           m_strings;
+		nv::hash_store< resource_id, mesh_source > m_source_map;
+		mesh_manager*                              m_mesh_manager;
+		string_table*                              m_strings;
 	};
 
Index: /trunk/nv/engine/model_manager.hh
===================================================================
--- /trunk/nv/engine/model_manager.hh	(revision 508)
+++ /trunk/nv/engine/model_manager.hh	(revision 509)
@@ -33,11 +33,27 @@
 		resource< material >         material;
 		transform                    local;
-		sint16                       attach_id;
-		float                        chance;
-		bool                         random_rotate_y;
-		bool                         force;
+		random_range< vec3 >         position;
+		random_range< vec3 >         rotation;
 		vector< model_node* >        children;
-
-		model_node() : attach_id( 0 ), chance( 1.0f ), random_rotate_y( false ), force( false ) {}
+		sint16                       attach_id = -1;
+		uint16                       weight    = 0;
+		float                        chance    = 1.0f;
+		bool                         force     = false;
+		bool                         choice    = false;
+
+		void clone_values_to( model_node* target ) const
+		{
+			target->mesh      = mesh;
+			target->material  = material;
+			target->local     = local;
+			target->rotation  = rotation;
+			target->position  = position;
+
+			target->attach_id = attach_id;
+			target->weight    = weight;
+			target->chance    = chance;
+			target->force     = force;
+			target->choice    = choice;
+		}
 
 		~model_node()
@@ -62,9 +78,10 @@
 		sint16                       parent_id;
 		transform					 local;
+		uint32                       flags;
 	};
 
 	struct flat_model
 	{
-		flat_model_element             elements[8];
+		flat_model_element             elements[16];
 		uint32                         count;
 		shash64                        attach;
@@ -73,4 +90,20 @@
 
 	NV_RTTI_DECLARE_NAME( model, "model" )
+
+	enum flat_model_flags
+	{
+		FMF_SELECTED    = 0x01,
+		FMF_FOCUS       = 0x02,
+		FMF_FAIL_CHANCE = 0x04,
+		FMF_FAIL_CHOICE = 0x08,
+		FMF_RANGE_GHOST = 0x10,
+	};
+
+	enum flatten_flags
+	{
+		FF_GENERATE_CHANCE = 0x02,
+		FF_GENERATE_CHOICE = 0x04,
+		FF_GENERATE_GHOST  = 0x08,
+	};
 
 	class model_manager : public lua_resource_manager< model >
@@ -85,5 +118,5 @@
 		virtual string_view get_storage_name() const { return "models"; }
 		virtual string_view get_resource_name() const { return "model"; }
-		static flat_model flatten( const model* m, random_base& rng )
+		static flat_model flatten( const model* m, random_base& rng, vector< const model_node* >* map = nullptr, uint32 gen_flags = 0, const model_node* select = nullptr )
 		{
 			flat_model result;
@@ -92,16 +125,35 @@
 			result.local = m->root;
 			result.count = 0;
-			flatten( result, m, rng, transform(), -1 );
+			flatten( result, m, rng, transform(), -1, map, gen_flags, 0, select );
 			return result;
 		}
 	protected:
-		static void flatten( flat_model& result, const model_node* m, random_base& rng, const transform& ptr, sint16 parent_id )
+		static void flatten( 
+			flat_model& result, 
+			const model_node* m, 
+			random_base& rng, 
+			const transform& ptr, 
+			sint16 parent_id, 
+			vector< const model_node* >* map,
+			uint32 gen_flags,
+			uint32 parent_flags,
+			const model_node* selected
+		)
 		{
 			if ( m->chance < 1.0f )
 				if ( rng.frand() > m->chance )
-					return;
+				{
+					if ( gen_flags & FF_GENERATE_CHANCE )
+						parent_flags |= FMF_FAIL_CHANCE;
+					else
+						return;
+				}
 			transform tr = ptr * m->local;
-			if ( m->random_rotate_y )
-				tr = tr * transform( math::angle_axis( nv::math::pi<float>() * 2.0f * rng.frand(), vec3( 0.0f, 1.0f, 0.0f ) ) );
+			vec3 position = rng.eval( m->position );
+			vec3 rotation = rng.eval( m->rotation );
+			transform rtr = transform( position, nv::quat( rotation ) );
+			tr = tr * rtr;
+
+			if ( m == selected ) parent_flags |= FMF_FOCUS;
 
 			if ( m->mesh || m->force )
@@ -114,10 +166,70 @@
 				re.parent_id = parent_id;
 				re.attach_id = m->attach_id;
+				re.flags = parent_flags;
+				if ( map ) map->push_back( m );
 				parent_id = sint16(id);
+
+				if ( m == selected ) 
+				{
+					re.flags |= FMF_SELECTED;
+					if ( gen_flags & FF_GENERATE_GHOST && 
+							( m->position.max != m->position.min || 
+							m->rotation.max != m->rotation.min ) )
+					{
+						uint32 min_id = result.count++;
+						flat_model_element& min_re = result.elements[min_id];
+						min_re.mesh = m->mesh;
+						min_re.material = m->material;
+						min_re.local = ( ptr * m->local ) * transform( m->position.min, nv::quat( m->rotation.min ) );
+						min_re.parent_id = parent_id;
+						min_re.attach_id = m->attach_id;
+						min_re.flags = parent_flags | FMF_RANGE_GHOST;
+
+						uint32 max_id = result.count++;
+						flat_model_element& max_re = result.elements[max_id];
+						max_re.mesh = m->mesh;
+						max_re.material = m->material;
+						max_re.local = ( ptr * m->local ) * transform( m->position.max, nv::quat( m->rotation.max ) );
+						max_re.parent_id = parent_id;
+						max_re.attach_id = m->attach_id;
+						max_re.flags = parent_flags | FMF_RANGE_GHOST;
+					}
+				}
 			}
-			for ( auto c : m->children )
+
+			if ( m->choice )
 			{
-				flatten( result, c, rng, tr, parent_id );
+				uint16 total_weight = 0;
+				for ( auto c : m->children ) 
+					total_weight += c->weight;
+				if ( total_weight > 0 )
+				{
+					sint32 roll = rng.srand( total_weight );
+					model_node* pick = nullptr;
+					for ( auto c : m->children )
+					{
+						roll -= c->weight;
+						if ( roll < 0 )
+						{
+							pick = c;
+							break;
+						}
+					}
+					if ( gen_flags & FF_GENERATE_CHOICE )
+						for ( auto c : m->children )
+						{
+							uint32 flags = parent_flags;
+							if ( c != pick ) flags |= FMF_FAIL_CHOICE;
+							flatten( result, c, rng, tr, parent_id, map, gen_flags, flags, selected );
+						}
+					else
+						flatten( result, pick, rng, tr, parent_id, map, gen_flags, parent_flags, selected );
+				}
 			}
+			else
+				for ( auto c : m->children )
+				{
+					flatten( result, c, rng, tr, parent_id, map, gen_flags, parent_flags, selected );
+				}
 		}
 
Index: /trunk/nv/engine/resource_system.hh
===================================================================
--- /trunk/nv/engine/resource_system.hh	(revision 508)
+++ /trunk/nv/engine/resource_system.hh	(revision 509)
@@ -114,4 +114,25 @@
 	protected:
 
+		virtual void remove( resource_id id )
+		{
+			auto m = m_store.find( shash64( id ) );
+			if ( m != m_store.end() )
+			{
+				release( m->second );
+				m_store.erase( shash64( id ) );
+			}
+		}
+
+		virtual void rename( resource_id id, resource_id new_id )
+		{
+			auto m = m_store.find( shash64( id ) );
+			if ( m != m_store.end() )
+			{
+				auto mdata = m->second;
+				m_store.erase( shash64( id ) );
+				add( new_id, mdata );
+			}
+		}
+
 		resource_type add( shash64 id, stored_type resource )
 		{
Index: /trunk/nv/gfx/debug_draw.hh
===================================================================
--- /trunk/nv/gfx/debug_draw.hh	(revision 508)
+++ /trunk/nv/gfx/debug_draw.hh	(revision 509)
@@ -36,4 +36,5 @@
 		void push_line( const vec3& a, const vec3& b, const vec3& color );
 		void push_aabox( const vec3& a, const vec3& b, const vec3& color );
+		void push_gizmo( const nv::transform& tr, float length );
 		~debug_data();
 	private:
Index: /trunk/nv/lua/lua_state.hh
===================================================================
--- /trunk/nv/lua/lua_state.hh	(revision 508)
+++ /trunk/nv/lua/lua_state.hh	(revision 509)
@@ -371,5 +371,6 @@
 			const_string get_string( string_view element, string_view defval = string_view() );
 			string128 get_string128( string_view element, string_view defval = string_view() );
-			
+			string64 get_string64( string_view element, string_view defval = string_view() );
+
 			char get_char( string_view element, char defval = ' ' );
 			int get_integer( string_view element, int defval = 0 );
Index: /trunk/nv/stl/math/constants.hh
===================================================================
--- /trunk/nv/stl/math/constants.hh	(revision 508)
+++ /trunk/nv/stl/math/constants.hh	(revision 509)
@@ -197,16 +197,30 @@
 		}
 
-		template < typename T >
+		template < typename T, typename enable_if< is_arithmetic<T>::value >::type* = nullptr >
 		constexpr T radians( T degrees )
 		{
-			static_assert( is_floating_point<T>::value, "Type expected to be floating point!" );
-			return degrees * static_cast<T>( 0.01745329251994329576923690768489 );
-		}
-
-		template < typename T >
+			static_assert( is_fp<T>::value, "Type expected to be floating point!" );
+			return degrees * T( 0.01745329251994329576923690768489 );
+		}
+
+		template < typename T, typename enable_if< is_arithmetic<T>::value >::type* = nullptr >
 		constexpr T degrees( T radians )
 		{
-			static_assert( is_floating_point<T>::value, "Type expected to be floating point!" );
-			return radians * static_cast<T>( 57.295779513082320876798154814105 );
+			static_assert( is_fp<T>::value, "Type expected to be floating point!" );
+			return radians * T( 57.295779513082320876798154814105 );
+		}
+
+		template < typename T, typename enable_if< is_vec<T>::value >::type* = nullptr >
+		constexpr T radians( T degrees )
+		{
+			static_assert( is_fp<T>::value, "Type expected to be floating point!" );
+			return degrees * value_type_t<T>( 0.01745329251994329576923690768489 );
+		}
+
+		template < typename T, typename enable_if< is_vec<T>::value >::type* = nullptr >
+		constexpr T degrees( T radians )
+		{
+			static_assert( is_fp<T>::value, "Type expected to be floating point!" );
+			return radians * value_type_t<T>( 57.295779513082320876798154814105 );
 		}
 
Index: /trunk/nv/stl/math/quaternion.hh
===================================================================
--- /trunk/nv/stl/math/quaternion.hh	(revision 508)
+++ /trunk/nv/stl/math/quaternion.hh	(revision 509)
@@ -320,5 +320,5 @@
 		inline T roll( const tquat<T>& q )
 		{
-			return T( atan( T( 2 ) * ( q.x * q.y + q.w * q.z ), q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z ) );
+			return T( ::nv::atan2( T( 2 ) * ( q.x * q.y + q.w * q.z ), q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z ) );
 		}
 
@@ -326,5 +326,5 @@
 		inline T pitch( const tquat<T>& q )
 		{
-			return T( atan( T( 2 ) * ( q.y * q.z + q.w * q.x ), q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z ) );
+			return T( ::nv::atan2( T( 2 ) * ( q.y * q.z + q.w * q.x ), q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z ) );
 		}
 
@@ -332,5 +332,5 @@
 		inline T yaw( const tquat<T>& q )
 		{
-			return asin( T( -2 ) * ( q.x * q.z - q.w * q.y ) );
+			return ::nv::asin( T( T( -2 ) * ( q.x * q.z - q.w * q.y ) ) );
 		}
 
Index: /trunk/src/core/random.cc
===================================================================
--- /trunk/src/core/random.cc	(revision 508)
+++ /trunk/src/core/random.cc	(revision 509)
@@ -271,4 +271,5 @@
 nv::random_base::seed_type nv::random_xor128::set_seed( seed_type seed /*= 0 */ )
 {
+	if ( seed == 0 ) seed = randomized_seed();
 	uint32 s = uint32( 4294967296 - seed );
 	m_state[0] = 123456789 * s;
Index: /trunk/src/engine/animation.cc
===================================================================
--- /trunk/src/engine/animation.cc	(revision 508)
+++ /trunk/src/engine/animation.cc	(revision 509)
@@ -34,4 +34,5 @@
 
 	nv::animator_data* animator = new nv::animator_data;
+	animator->id = table.get_string64( "id" );
 	animator->poses = poses;
 	if ( poses == nullptr )
Index: /trunk/src/engine/default_resource_manager.cc
===================================================================
--- /trunk/src/engine/default_resource_manager.cc	(revision 508)
+++ /trunk/src/engine/default_resource_manager.cc	(revision 509)
@@ -9,5 +9,5 @@
 using namespace nv;
 
-default_resource_manager::default_resource_manager( context* context )
+default_resource_manager::default_resource_manager( context* context, bool clear_material_paths )
 {
 	m_images        = register_resource_handler< image_data >( new image_manager );
@@ -15,5 +15,5 @@
 	m_binds         = register_resource_handler< animator_bind_data >( new animator_bind_manager );
 	m_animators     = register_resource_handler< animator_data >( new animator_manager );
-	m_materials     = register_resource_handler< material >( new material_manager );
+	m_materials     = register_resource_handler< material >( new material_manager( clear_material_paths ) );
 	m_programs      = register_resource_handler< program >( new program_manager( context ) );
 	m_gpu_meshes    = register_resource_handler< gpu_mesh >( new gpu_mesh_manager( context, m_meshes ) );
@@ -27,4 +27,19 @@
 	m_lua = lua;
 
+	m_lua->register_enum( "RND_LINEAR",     static_cast<int>( random_dist::LINEAR ) );
+	m_lua->register_enum( "RND_GAUSSIAN",   static_cast<int>( random_dist::GAUSSIAN ) );
+	m_lua->register_enum( "RND_RGAUSSIAN",  static_cast<int>( random_dist::RGAUSSIAN ) );
+	m_lua->register_enum( "RND_STEP_1",     static_cast<int>( random_dist::STEP_1 ) );
+	m_lua->register_enum( "RND_STEP_2",     static_cast<int>( random_dist::STEP_2 ) );
+	m_lua->register_enum( "RND_STEP_3",     static_cast<int>( random_dist::STEP_3 ) );
+	m_lua->register_enum( "RND_STEP_4",     static_cast<int>( random_dist::STEP_4 ) );
+	m_lua->register_enum( "RND_LINEAR",     static_cast<int>( random_dist::LINEAR ) );
+	m_lua->register_enum( "RND_MGAUSSIAN",  static_cast<int>( random_dist::MGAUSSIAN ) );
+	m_lua->register_enum( "RND_MRGAUSSIAN", static_cast<int>( random_dist::MRGAUSSIAN ) );
+	m_lua->register_enum( "RND_MSTEP_1",    static_cast<int>( random_dist::MSTEP_1 ) );
+	m_lua->register_enum( "RND_MSTEP_2",    static_cast<int>( random_dist::MSTEP_2 ) );
+	m_lua->register_enum( "RND_MSTEP_3",    static_cast<int>( random_dist::MSTEP_3 ) );
+	m_lua->register_enum( "RND_MSTEP_4",    static_cast<int>( random_dist::MSTEP_4 ) );
+	
 	m_lua->register_enum( "INT_NONE",       static_cast<int>( interpolation::NONE ) );
 	m_lua->register_enum( "INT_LINEAR",     static_cast<int>( interpolation::LINEAR ) );
Index: /trunk/src/engine/material_manager.cc
===================================================================
--- /trunk/src/engine/material_manager.cc	(revision 508)
+++ /trunk/src/engine/material_manager.cc	(revision 509)
@@ -63,4 +63,5 @@
 	if ( table.is_string( "path" ) )
 	{
+		m->id = table.get_string128( "id" );
 		string128 path = table.get_string128( "path" );
 		for ( uint32 i = 0; i < 5; ++i )
@@ -77,5 +78,6 @@
 				if ( i != TEX_EMISSIVE )
 					NV_LOG_ERROR( "Texture file not found! : ", m->paths[i] );
-				//m->paths[i].clear();
+				if ( m_clear_paths )
+					m->paths[i].clear();
 			}
 		}
Index: /trunk/src/engine/mesh_manager.cc
===================================================================
--- /trunk/src/engine/mesh_manager.cc	(revision 508)
+++ /trunk/src/engine/mesh_manager.cc	(revision 509)
@@ -114,4 +114,7 @@
 			result->node_names[(*nd)[i].name] = i;
 	}
+	
+	resource< mesh_data > rmesh = add( id, result );
+
 	for ( uint32 i = 0; i < loader->get_mesh_count(); ++i )
 	{
@@ -122,8 +125,8 @@
 		auto mesh = m_mesh_manager->add( shash64( id.get_hash() + i ), data );
 		result->meshes.push_back( mesh );
+		m_source_map[mesh.id()] = mesh_source{ rmesh, i };
 	}
+	result->path.assign( id );
 	delete loader;
-	if ( result )
-		add( id, result );
 	return result != nullptr;
 }
Index: /trunk/src/engine/model_manager.cc
===================================================================
--- /trunk/src/engine/model_manager.cc	(revision 508)
+++ /trunk/src/engine/model_manager.cc	(revision 509)
@@ -13,8 +13,11 @@
 bool nv::model_manager::load_resource( lua::table_guard& table, shash64 id )
 {
+	auto vec4_to_quat = [] ( const vec4& v ) { return quat( v.w, v.x, v.y, v.z ); };
+
 	model* gm = new model;
 	gm->attach = table.get_string_hash_64( "attach" );
-	gm->root = transform( table.get<vec3>( "position", vec3() ) );
-
+	gm->root.set_position( table.get<vec3>( "root_position", vec3() ) );
+	gm->root.set_orientation( vec4_to_quat( table.get<vec4>( "root_orientation", vec4(0.0f,0.0f,0.0f,1.0f) ) ) );
+	
 	resource< mesh_data > def_data;
 	if ( table.is_string( "path" ) )
@@ -45,4 +48,6 @@
 void nv::model_manager::read_model_node( lua::table_guard& table, model_node* node, resource< mesh_data > def_data )
 {
+	auto vec4_to_quat = [] ( const vec4& v ) { return quat( v.w, v.x, v.y, v.z ); };
+
 	resource< data_channel_set > cmesh;
 	resource< material >         cmaterial;
@@ -59,4 +64,26 @@
 		attach_id = info.parent_id;
 	}
+
+	if ( table.has_field( "local_position" ) )
+		node->local.set_position( table.get<vec3>( "local_position", vec3() ) );
+	if ( table.has_field( "local_orientation" ) )
+		node->local.set_orientation( vec4_to_quat( table.get<vec4>( "local_orientation", vec4( 0.0f, 0.0f, 0.0f, 1.0f ) ) ) );
+
+	if ( table.has_field( "position" ) )
+	{
+		node->position.min = table.get<vec3>( "position", vec3() );
+		node->position.max = node->position.min;
+	}
+	if ( table.has_field( "rotation" ) )
+	{
+		node->rotation.min = table.get<vec3>( "rotation", vec3() );
+		node->rotation.max = node->rotation.min;
+	}
+	if ( table.has_field( "position_min" ) )  node->position.min  = table.get<vec3>( "position_min", vec3() );
+	if ( table.has_field( "position_max" ) )  node->position.max  = table.get<vec3>( "position_max", vec3() );
+	if ( table.has_field( "position_dist" ) ) node->position.dist = random_dist( table.get_unsigned( "position_dist", 0 ) );
+	if ( table.has_field( "rotation_min" ) )  node->rotation.min  = table.get<vec3>( "rotation_min", vec3() );
+	if ( table.has_field( "rotation_max" ) )  node->rotation.max  = table.get<vec3>( "rotation_max", vec3() );
+	if ( table.has_field( "rotation_dist" ) ) node->rotation.dist = random_dist( table.get_unsigned( "rotation_dist", 0 ) );
 
 	if ( table.has_field( "attach" ) )
@@ -76,11 +103,11 @@
 	}
 
-	node->force = table.get_boolean( "force", false );
-	node->chance = table.get_float( "chance", 1.0f );
-	node->random_rotate_y = table.get_boolean( "random_rotate", false );
-
-	node->mesh = cmesh;
+	node->force     = table.get_boolean( "force", false );
+	node->choice    = table.get_boolean( "choice", false );
+	node->chance    = table.get_float( "chance", 1.0f );
+	node->weight    = table.get_unsigned( "weight", 1 );
+	node->mesh      = cmesh;
 	node->attach_id = attach_id;
-	node->material = cmaterial;
+	node->material  = cmaterial;
 
 	for ( uint32 i = 1; i <= table.get_size(); ++i )
Index: /trunk/src/gfx/debug_draw.cc
===================================================================
--- /trunk/src/gfx/debug_draw.cc	(revision 508)
+++ /trunk/src/gfx/debug_draw.cc	(revision 509)
@@ -86,4 +86,19 @@
 }
 
+void nv::debug_data::push_gizmo( const transform& tr, float length )
+{
+	vec3 s( 0.0f,   0.0f,   0.0f );
+	vec3 r( length, 0.0f,   0.0f );
+	vec3 g( 0.0f,   length, 0.0f );
+	vec3 b( 0.0f,   0.0f,   length );
+	s = s * tr;
+	r = r * tr;
+	g = g * tr;
+	b = b * tr;
+	push_line( s, r, vec3( 1.0f, 0.0f, 0.0f ) );
+	push_line( s, g, vec3( 0.0f, 1.0f, 0.0f ) );
+	push_line( s, b, vec3( 0.0f, 0.0f, 1.0f ) );
+}
+
 nv::debug_data::~debug_data()
 {
Index: /trunk/src/lua/lua_state.cc
===================================================================
--- /trunk/src/lua/lua_state.cc	(revision 508)
+++ /trunk/src/lua/lua_state.cc	(revision 509)
@@ -324,4 +324,24 @@
 	return result;
 }
+
+nv::string64 nv::lua::table_guard::get_string64( string_view element, string_view defval /*= string_view() */ )
+{
+	lua_getfield( m_state, -1, element.data() );
+	size_t l = 0;
+	const char* str = nullptr;
+	if ( lua_type( m_state, -1 ) == LUA_TSTRING )
+	{
+		str = lua_tolstring( m_state, -1, &l );
+	}
+	else
+	{
+		l = defval.size();
+		str = defval.data();
+	}
+	string64 result( str, l );
+	lua_pop( m_state, 1 );
+	return result;
+}
+
 
 char lua::table_guard::get_char( string_view element, char defval /*= "" */ )
