Index: trunk/nv/engine/animation.hh
===================================================================
--- trunk/nv/engine/animation.hh	(revision 507)
+++ trunk/nv/engine/animation.hh	(revision 508)
@@ -354,5 +354,5 @@
 	};
 
-	class animator_manager : public nv::lua_resource_manager< animator_data >
+	class animator_manager : public file_resource_manager< animator_data, true, lua_resource_manager_base >
 	{
 	public:
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 508)
@@ -0,0 +1,70 @@
+// Copyright (C) 2016-2016 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+/**
+* @file default_resource_manager.hh
+* @author Kornel Kisielewicz
+* @brief engine default resource manager
+*/
+
+#ifndef NV_ENGINE_DEFAULT_RESOURCE_MANAGER_HH
+#define NV_ENGINE_DEFAULT_RESOURCE_MANAGER_HH
+
+#include <nv/common.hh>
+#include <nv/interface/context.hh>
+#include <nv/lua/lua_state.hh>
+#include <nv/engine/resource_system.hh>
+#include <nv/engine/program_manager.hh>
+#include <nv/stl/array.hh>
+#include <nv/gfx/skeleton_instance.hh>
+#include <nv/engine/animation.hh>
+#include <nv/engine/mesh_manager.hh>
+#include <nv/engine/material_manager.hh>
+#include <nv/engine/model_manager.hh>
+
+namespace nv
+{
+
+	class default_resource_manager : public resource_manager
+	{
+	public:
+		explicit default_resource_manager( context* context );
+
+		void initialize( lua::state* lua );
+		void add_path( const string_view& path );
+
+		resource< gpu_mesh > get_gpu_mesh( resource< data_channel_set > mesh )
+		{
+			return m_gpu_meshes->load_resource( mesh );
+		}
+		resource< gpu_material > get_gpu_material( resource< material > m )
+		{
+			return m_gpu_materials->load_resource( m );
+		}
+
+		nv::string_view resolve( nv::shash64 h )
+		{
+			return m_mesh_datas->resolve( h );
+		}
+
+		void reload_data();
+	private:
+		lua::state*            m_lua;
+		image_manager*         m_images;
+		material_manager*      m_materials;
+		animator_bind_manager* m_binds;
+		animator_manager*      m_animators;
+		program_manager*       m_programs;
+		gpu_mesh_manager*      m_gpu_meshes;
+		gpu_material_manager*  m_gpu_materials;
+		mesh_manager*          m_meshes;
+		mesh_data_manager*     m_mesh_datas;
+		model_manager*         m_models;
+	};
+
+}
+
+#endif // NV_ENGINE_DEFAULT_RESOURCE_MANAGER_HH
Index: trunk/nv/engine/light.hh
===================================================================
--- trunk/nv/engine/light.hh	(revision 508)
+++ trunk/nv/engine/light.hh	(revision 508)
@@ -0,0 +1,258 @@
+// Copyright (C) 2016-2016 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+/**
+ * @file animation.hh
+ * @author Kornel Kisielewicz
+ * @brief animation
+ */
+
+#ifndef NV_ENGINE_LIGHT_HH
+#define NV_ENGINE_LIGHT_HH
+
+#include <nv/common.hh>
+#include <nv/core/resource.hh>
+#include <nv/stl/math.hh>
+#include <nv/stl/array.hh>
+#include <nv/interface/context.hh>
+
+namespace nv
+{
+
+	enum class shadow_type
+	{
+		NONE,
+		ACTIVE,
+		PASSIVE,
+	};
+
+	struct light_data
+	{
+		vec3 position;
+		vec3 color;
+		vec4 direction;
+		float angle;
+		float umbra_angle;
+		float range;
+		uint32 smooth;
+		shadow_type shadow;
+
+		light_data() : color( 1.0 ), direction( 0.0f, -1.0f, 0.0f, 1.0f ), angle( 0.6f ), umbra_angle( 0.2f ), range( 4.0 ), smooth( 0 ), shadow( shadow_type::ACTIVE ) {}
+		light_data( const vec3& a_position, const vec3& a_color, shadow_type type )
+			: position( a_position ), color( a_color )
+			, direction( 0.0f, -1.0f, 0.0f, 1.0f ), angle( 0.6f ), umbra_angle( 0.2f ), range( 4.0 ), smooth( 0 ), shadow( type ) {}
+		light_data( const light_data& ) = default;
+	};
+
+	struct light_data_distance_compare
+	{
+		vec3 source;
+		light_data_distance_compare( const vec3& source ) : source( source ) {}
+		bool operator()( const light_data* l, const light_data* r )
+		{
+			vec3 dl( l->position - source );
+			vec3 dr( r->position - source );
+			return math::length_sq( dl ) < math::length_sq( dr );
+		}
+	};
+
+	class gpu_light_block
+	{
+	public:
+		gpu_light_block() : m_buffer_index(0), m_max_size(0) {}
+		const camera& get_camera( uint32 i )       const { return m_cameras[i]; }
+		vec3          get_position( uint32 i )     const { return vec3( m_blocks[i].position ); }
+		const vec4&   get_shadow_index( uint32 i ) const { return m_blocks[i].shadow; }
+		uint32 index() const { return m_buffer_index; }
+
+		void initialize( context* ctx, uint32 max_size, uint32 index )
+		{
+			m_buffer_index = index;
+			m_max_size = max_size;
+			m_cameras.resize( m_max_size );
+			m_blocks.resize( m_max_size );
+			if ( m_buffer ) ctx->release( m_buffer );
+			m_buffer = ctx->create_buffer( UNIFORM_BUFFER, STREAM_DRAW, sizeof( block_data ) * m_max_size );
+		}
+
+		void update( array_view< light_data* > lights )
+		{
+			uint32 count = nv::min< uint32 >( lights.size(), m_max_size );
+			uint32 shadow_counter[4] = { 0, 0, 0, 0 };
+			for ( size_t i = 0; i < count; ++i )
+			{
+				m_cameras[i].set_lookat( vec3(), vec3( lights[i]->direction ), vec3( 1.0f, 0.0f, 0.0f ) );
+				m_cameras[i].set_perspective( 150.f, 1.0f, 0.01f, 5.0f );
+
+				m_blocks[i].position  = vec4( lights[i]->position, 1.0f );
+				m_blocks[i].color     = vec4( lights[i]->color, 1.0f );
+				m_blocks[i].direction = lights[i]->direction;
+				m_blocks[i].params    = vec4( lights[i]->angle, lights[i]->umbra_angle, lights[i]->range, 1.0f );
+				m_blocks[i].mvp       = m_cameras[i].get_projection() * m_cameras[i].get_view();
+				uint32 index = lights[i]->smooth;
+				m_blocks[i].shadow    = vec4( float( index ), float( shadow_counter[index]++ ), 0.0f, 0.0f );
+			}
+		}
+
+		void bind( context* ctx ) const
+		{
+			ctx->bind( m_buffer, m_buffer_index );
+		}
+
+		void update( context* ctx )
+		{
+			ctx->update( m_buffer, m_blocks.data(), 0, sizeof( block_data ) * m_max_size );
+			ctx->bind( m_buffer, m_buffer_index );
+		}
+
+	private:
+		struct block_data
+		{
+			vec4 position;
+			vec4 color;
+			vec4 direction;
+			vec4 params; // 0 - angle, 1 - umbra angle, 2 - attenuation
+			mat4 mvp;
+			vec4 shadow;
+		};
+
+		dynamic_array< camera >     m_cameras;
+		dynamic_array< block_data > m_blocks;
+		buffer m_buffer;
+		uint32 m_buffer_index;
+		uint32 m_max_size;
+	};
+
+	struct gpu_light_geometry
+	{
+	public:
+		static const unsigned lgeo_vertices = 4;
+		static const unsigned lgeo_indices = 6;
+
+		void initialize( uint32 max_size )
+		{
+			m_max_size = max_size;
+			m_light_data.resize( lgeo_vertices * m_max_size );
+			m_light_idata.resize( lgeo_indices * m_max_size );
+		}
+		uint32 max_size() const { return m_max_size; }
+
+		vertex_array get_va() const { return m_va; }
+		uint32 index_stride() const { return lgeo_indices; }
+
+		void update( array_view< light_data* > lights )
+		{
+			uint32 count = nv::min< uint32 >( lights.size(), m_max_size );
+			for ( size_t i = 0; i < count; ++i )
+			{
+				vec3 nlp = lights[i]->position;
+				float radius = lights[i]->range;
+				nlp.y = 0.1f;
+				vec3 r1( radius, 0.0f, radius );
+				vec3 r2( radius, 0.0f, -radius );
+
+				m_light_data[i * lgeo_vertices + 0] = light_vertex{ vec4( nlp - r1, float( i ) ) };
+				m_light_data[i * lgeo_vertices + 1] = light_vertex{ vec4( nlp - r2, float( i ) ) };
+				m_light_data[i * lgeo_vertices + 2] = light_vertex{ vec4( nlp + r1, float( i ) ) };
+				m_light_data[i * lgeo_vertices + 3] = light_vertex{ vec4( nlp + r2, float( i ) ) };
+
+				uint16 indices[lgeo_indices] = { 0, 1, 2, 2, 3, 0 };
+				for ( uint16 j = 0; j < lgeo_indices; ++j )
+				{
+					m_light_idata[i * lgeo_indices + j] = uint16( indices[j] + i * lgeo_vertices );
+				}
+			}
+		}
+
+
+		void update( context* ctx, uint32 count )
+		{
+			if ( m_va.is_nil() )
+			{
+				// remove data()
+				m_position_buffer = ctx->create_buffer( VERTEX_BUFFER, STREAM_DRAW, m_light_data.size() * sizeof( light_vertex ), m_light_data.data() );
+				m_index_buffer = ctx->create_buffer( INDEX_BUFFER, STREAM_DRAW, m_light_idata.size() * sizeof( light_index ), m_light_idata.data() );
+				vertex_array_desc va_desc;
+				va_desc.add_vertex_buffers< light_vertex >( m_position_buffer, true );
+				va_desc.set_index_buffer( m_index_buffer, type_to_enum< light_index >::type, true );
+				m_va = ctx->create_vertex_array( va_desc );
+			}
+			else
+			{
+				ctx->update( m_position_buffer, m_light_data.data(), 0, lgeo_vertices * count * sizeof( light_vertex ) );
+				ctx->update( m_index_buffer, m_light_idata.data(), 0, lgeo_indices  * count * sizeof( uint16 ) );
+			}
+
+		}
+
+	private:
+		struct light_vertex
+		{
+			vec4  position;  // position, w = index
+		};
+		typedef uint16 light_index;
+
+		uint32       m_max_size;
+		buffer       m_position_buffer;
+		buffer       m_index_buffer;
+		vertex_array m_va;
+
+		dynamic_array< light_vertex > m_light_data;
+		dynamic_array< light_index >  m_light_idata;
+
+	};
+
+	class gpu_light_data
+	{
+	public:
+		void initialize( context* ctx, uint32 max_size, uint32 index )
+		{
+			m_active = 0;
+			m_block.initialize( ctx, max_size, index );
+			m_geometry.initialize( max_size );
+		}
+
+		void update( context* ctx, array_view< light_data* > lights )
+		{
+			m_active = nv::min<uint32>( m_geometry.max_size(), lights.size() );
+			if ( m_active == 0 ) return;
+			m_block.update( lights );
+			m_geometry.update( lights );
+
+			m_block.update( ctx );
+			m_geometry.update( ctx, m_active );
+		}
+
+		void filter_update( context* ctx, array_view< light_data* > lights, shadow_type type )
+		{
+			uint32 count = 0;
+			array< light_data*, 128 > filtered_lights;
+			uint32 size = nv::min< uint32 >( lights.size(), geometry().max_size() );
+
+			for ( auto light : lights )
+			{
+				if ( light->shadow == type )
+					filtered_lights[count++] = light;
+				if ( count >= size ) break;
+			}
+			update( ctx, array_view< light_data* >( filtered_lights.data(), count ) );
+		}
+
+		const gpu_light_block& block() const { return m_block; }
+		const gpu_light_geometry& geometry() const { return m_geometry; }
+		uint32 active_count() const { return m_active; }
+
+	protected:
+		uint32             m_active;
+		gpu_light_block    m_block;
+		gpu_light_geometry m_geometry;
+	};
+
+
+}
+
+#endif // NV_ENGINE_LIGHT_HH
+
Index: trunk/nv/engine/mesh_manager.hh
===================================================================
--- trunk/nv/engine/mesh_manager.hh	(revision 507)
+++ trunk/nv/engine/mesh_manager.hh	(revision 508)
@@ -26,5 +26,5 @@
 	enum shader_geo
 	{
-		NORMAL = 0,
+		DIRECT = 0,
 		BONE = 1,
 	};
Index: trunk/nv/engine/model_manager.hh
===================================================================
--- trunk/nv/engine/model_manager.hh	(revision 507)
+++ trunk/nv/engine/model_manager.hh	(revision 508)
@@ -96,5 +96,5 @@
 		}
 	protected:
-		static void flatten( flat_model& result, const model_node* m, random_base& rng, const transform& ptr, sint32 parent_id )
+		static void flatten( flat_model& result, const model_node* m, random_base& rng, const transform& ptr, sint16 parent_id )
 		{
 			if ( m->chance < 1.0f )
@@ -114,5 +114,5 @@
 				re.parent_id = parent_id;
 				re.attach_id = m->attach_id;
-				parent_id = id;
+				parent_id = sint16(id);
 			}
 			for ( auto c : m->children )
Index: trunk/nv/engine/render_pass.hh
===================================================================
--- trunk/nv/engine/render_pass.hh	(revision 508)
+++ trunk/nv/engine/render_pass.hh	(revision 508)
@@ -0,0 +1,36 @@
+// Copyright (C) 2016-2016 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+#ifndef NV_ENGINE_RENDER_PASS_HH
+#define NV_ENGINE_RENDER_PASS_HH
+
+#include <nv/common.hh>
+#include <nv/stl/flags.hh>
+#include <nv/core/resource.hh>
+#include <nv/interface/context.hh>
+#include <nv/interface/device.hh>
+
+namespace nv
+{
+
+	struct render_pass
+	{
+		render_state        rstate;
+		clear_state         cstate;
+		framebuffer         fbuffer;
+		output_slot         output[8];
+		uint32              output_count;
+		flags< 32 >         exclude;
+		flags< 32 >         any;
+		resource< program > programs[4];
+		texture             binds[16];
+	};
+
+}
+
+
+
+#endif // NV_ENGINE_RENDER_PASS_HH
Index: trunk/nv/engine/renderer.hh
===================================================================
--- trunk/nv/engine/renderer.hh	(revision 507)
+++ trunk/nv/engine/renderer.hh	(revision 508)
@@ -18,19 +18,101 @@
 #include <nv/interface/device.hh>
 #include <nv/interface/context.hh>
+#include <nv/engine/program_manager.hh>
+#include <nv/engine/material_manager.hh>
+#include <nv/engine/mesh_manager.hh>
+#include <nv/engine/render_pass.hh>
 
 namespace nv
 {
-	struct render_pass
+
+	struct renderer_element
 	{
-		render_state rstate;
-		clear_state  cstate;
-		framebuffer  fbuffer;
-		output_slot  output[8];
-		uint32       output_count;
+		resource< gpu_mesh >     mesh;
+		resource< gpu_material > mat;
+		uint32                   layer;
+		sint32                   bone_offset;
+		transform                model;
+		flags< 32 >              flags;
+	};
 
+	struct renderer_direct_element
+	{
+		vertex_array             va;
+		uint32                   count;
+		uint32                   instances;
+		uint32                   first;
+		resource< gpu_material > material;
+		resource< program >      programs[4];
+		transform                model;
+		flags< 32 >              flags;
+	};
+
+	struct renderer_element_compare
+	{
+		bool operator()( const renderer_element& l, const renderer_element& r )
+		{
+			if ( l.layer < r.layer ) return true;
+			if ( l.layer > r.layer ) return false;
+			if ( *l.flags.data() < *r.flags.data() ) return true;
+			if ( *l.flags.data() > *r.flags.data() ) return false;
+			if ( l.mat < r.mat ) return true;
+			if ( l.mat > r.mat ) return false;
+			return false;
+		}
 	};
 
 	class renderer
 	{
+	public:
+		enum stats
+		{
+			DRAW_CALL,
+			TRIANGLE_COUNT,
+			MAX
+		};
+
+		explicit renderer( context* ctx );
+		void start_frame()
+		{
+			for ( uint32& s : m_statistics ) s = 0;
+		}
+
+		void push_element( const nv::renderer_element& element )
+		{
+			m_elements.push_back( element );
+		}
+
+		void sort();
+		void render( const scene_state& ss, const render_pass& pass );
+		void render( const scene_state& ss, const render_pass& pass, vertex_array va, uint32 va_count, uint32 program_idx = 0 );
+		void render_quad( const scene_state& ss, const render_pass& pass, uint32 program_idx = 0 )
+		{
+			render( ss, pass, m_quad, 6, program_idx );
+		}
+		void render_direct( const scene_state& ss, const render_pass& pass, const nv::array_view< renderer_direct_element >& elements, uint32 program_idx = 0 );
+
+		uint32 get_stat( stats s ) const { return m_statistics[s]; }
+
+		void end_frame()
+		{
+			m_elements.clear();
+		}
+		void reset()
+		{
+			m_elements.clear();
+		}
+
+	protected:
+		bool match( flags<32> f, const render_pass& pass )
+		{
+			if ( ( f & pass.any ).empty() ) return false;
+			if ( ( f & pass.exclude ).any() ) return false;
+			return true;
+		}
+
+		context*                    m_context;
+		vertex_array                m_quad;
+		vector< renderer_element >  m_elements;
+		uint32                      m_statistics[MAX];
 
 	};
Index: trunk/nv/engine/shadow.hh
===================================================================
--- trunk/nv/engine/shadow.hh	(revision 508)
+++ trunk/nv/engine/shadow.hh	(revision 508)
@@ -0,0 +1,44 @@
+// Copyright (C) 2016-2016 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+#ifndef NV_ENGINE_SHADOW_HH
+#define NV_ENGINE_SHADOW_HH
+
+#include <nv/common.hh>
+#include <nv/stl/math.hh>
+#include <nv/stl/vector.hh>
+#include <nv/core/resource.hh>
+#include <nv/interface/context.hh>
+#include <nv/engine/render_pass.hh>
+
+namespace nv
+{
+
+	class shadow_data
+	{
+	public:
+		shadow_data() : m_map_size(0) {}
+		void initialize( context* context, uint32 count, uint32 map_size, const render_pass& pass_base );
+		void initialize( context* context, uint32 count, uint32 map_size, const render_pass& pass_base, const render_pass& color_base );
+		uint32 size() const { return m_passes.size(); }
+		uint32 map_size() const { return m_map_size; }
+		texture textures() const { return m_maps; }
+		texture color_textures() const { return m_color_maps; }
+		bool has_color() const { return m_color_passes.size() > 0; }
+		const render_pass& pass( uint32 i ) const { return m_passes[i]; }
+		const render_pass& color_pass( uint32 i ) const { return m_color_passes[i]; }
+	protected:
+		uint32                m_map_size;
+		texture               m_maps;
+		texture               m_color_maps;
+		vector< render_pass > m_passes;
+		vector< render_pass > m_color_passes;
+	};
+
+
+}
+
+#endif // NV_ENGINE_SHADOW_HH
Index: trunk/nv/interface/device.hh
===================================================================
--- trunk/nv/interface/device.hh	(revision 507)
+++ trunk/nv/interface/device.hh	(revision 508)
@@ -63,27 +63,20 @@
 	enum texture_slot
 	{
-		TEX_DIFFUSE   = 0,
-		TEX_NORMAL    = 1,
-		TEX_METALLIC  = 2,
-		TEX_SPECULAR  = 2, // obsolete
-		TEX_ROUGHNESS = 3,
-		TEX_GLOSS     = 3, // obsolete
-		TEX_EMISSIVE  = 4,
-		TEXTURE_0     = 0,
-		TEXTURE_1     = 1,
-		TEXTURE_2     = 2,
-		TEXTURE_3     = 3,
-		TEXTURE_4     = 4,
-		TEXTURE_5     = 5,
-		TEXTURE_6     = 6,
-		TEXTURE_7     = 7,
-		TEXTURE_8     = 8,
-		TEXTURE_9     = 9,
-		TEXTURE_10    = 10,
-		TEXTURE_11    = 11,
-		TEXTURE_12    = 12,
-		TEXTURE_13    = 13,
-		TEXTURE_14    = 14,
-		TEXTURE_15    = 15,
+		TEXTURE_0     = 0,  TEX_DIFFUSE   = 0,
+		TEXTURE_1     = 1,  TEX_NORMAL    = 1,
+		TEXTURE_2     = 2,  TEX_METALLIC  = 2,
+		TEXTURE_3     = 3,  TEX_ROUGHNESS = 3,
+		TEXTURE_4     = 4,  TEX_EMISSIVE  = 4,
+		TEXTURE_5     = 5,  TEX_DEPTH     = 5,
+		TEXTURE_6     = 6,  TEX_OCCLUSION = 6,
+		TEXTURE_7     = 7,  TEX_FINAL     = 7,
+		TEXTURE_8     = 8,  TEX_CUSTOM_0  = 8,
+		TEXTURE_9     = 9,  TEX_CUSTOM_1  = 9,
+		TEXTURE_10    = 10, TEX_CUSTOM_2  = 10,
+		TEXTURE_11    = 11, TEX_CUSTOM_3  = 11,
+		TEXTURE_12    = 12, TEX_CUSTOM_4  = 12,
+		TEXTURE_13    = 13, TEX_CUSTOM_5  = 13,
+		TEXTURE_14    = 14, TEX_CUSTOM_6  = 14,
+		TEXTURE_15    = 15, TEX_CUSTOM_7  = 15,
 		MAX_TEXTURES  = 16,
 	};
@@ -355,8 +348,9 @@
 			factory_link_map[ "nv_t_normal"   ] = new engine_link_uniform_int<int( TEX_NORMAL )>();
 			factory_link_map[ "nv_t_metallic" ] = new engine_link_uniform_int<int( TEX_METALLIC )>();
-			factory_link_map[ "nv_t_specular" ] = new engine_link_uniform_int<int( TEX_METALLIC )>();
 			factory_link_map[ "nv_t_roughness"] = new engine_link_uniform_int<int( TEX_ROUGHNESS )>();
-			factory_link_map[ "nv_t_gloss"    ] = new engine_link_uniform_int<int( TEX_ROUGHNESS )>();
 			factory_link_map[ "nv_t_emissive" ] = new engine_link_uniform_int<int( TEX_EMISSIVE )>();
+			factory_link_map[ "nv_t_depth"    ] = new engine_link_uniform_int<int( TEX_DEPTH )>();
+			factory_link_map[ "nv_t_occlusion"] = new engine_link_uniform_int<int( TEX_OCCLUSION )>();
+			factory_link_map[ "nv_t_final"    ] = new engine_link_uniform_int<int( TEX_FINAL )>();
 		}
 		void destroy_engine_uniforms()
Index: trunk/nv/io/c_stream.hh
===================================================================
--- trunk/nv/io/c_stream.hh	(revision 507)
+++ trunk/nv/io/c_stream.hh	(revision 508)
@@ -16,4 +16,5 @@
 #include <nv/common.hh>
 #include <nv/stl/stream.hh>
+#include <nv/stl/string.hh>
 
 namespace nv
@@ -39,7 +40,7 @@
 		virtual void flush();
 	private:
-		void*       m_file;      //!< FILE* structure, masked
-		const char* m_file_name; //!< File name
-		size_t      m_file_size; //!< Cached file size
+		void*        m_file;      //!< FILE* structure, masked
+		const_string m_file_name; //!< File name
+		size_t       m_file_size; //!< Cached file size
 	};
 
Index: trunk/nv/stl/flags.hh
===================================================================
--- trunk/nv/stl/flags.hh	(revision 507)
+++ trunk/nv/stl/flags.hh	(revision 508)
@@ -183,4 +183,18 @@
 		}
 
+		bool any() const
+		{
+			for ( auto c : m_data )
+				if ( c != 0 ) return true;
+			return false;
+		}
+
+		bool empty() const
+		{
+			for ( auto c : m_data )
+				if ( c != 0 ) return false;
+			return true;
+		}
+
 		const data_type* data() const
 		{
@@ -212,4 +226,10 @@
 			return reference( this, idx );
 		}
+
+		template < uint32 SIZE, typename T >
+		inline friend flags< SIZE, T > operator &( const flags< SIZE, T >& lhs, const flags< SIZE, T >& rhs );
+
+		template < uint32 SIZE, typename T >
+		inline friend flags< SIZE, T > operator |( const flags< SIZE, T >& lhs, const flags< SIZE, T >& rhs );
 
 	private:
@@ -217,4 +237,23 @@
 	};
 
+	template < uint32 SIZE, typename T >
+	inline flags< SIZE, T > operator &( const flags< SIZE, T >& lhs, const flags< SIZE, T >& rhs )
+	{
+		flags< SIZE, T > result( lhs );
+		for ( uint32 i = 0; i < result.data_size; ++i )
+			result.m_data[i] &= rhs.m_data[i];
+		return result;
+	}
+
+	template < uint32 SIZE, typename T >
+	inline flags< SIZE, T > operator |( const flags< SIZE, T >& lhs, const flags< SIZE, T >& rhs )
+	{
+		flags< SIZE, T > result( lhs );
+		for ( uint32 i = 0; i < result.data_size; ++i )
+			result.m_data[i] |= rhs.m_data[i];
+		return result;
+	}
+
+
 } // namespace nv
 
Index: trunk/src/engine/animation.cc
===================================================================
--- trunk/src/engine/animation.cc	(revision 507)
+++ trunk/src/engine/animation.cc	(revision 508)
@@ -41,5 +41,6 @@
 		{
 			nv::c_file_system fs;
-			nv::stream* poses_file = fs.open( poses_path );
+			nv::stream* poses_file = open_stream( fs, poses_path );
+			NV_ASSERT_ALWAYS( poses_file, "CANT FIND POSES FILE!" );
 			nv::nmd_loader* ploader = new nv::nmd_loader( nullptr );
 			ploader->load( *poses_file );
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 508)
@@ -0,0 +1,68 @@
+// Copyright (C) 2016-2016 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+#include "nv/engine/default_resource_manager.hh"
+
+using namespace nv;
+
+default_resource_manager::default_resource_manager( context* context )
+{
+	m_images        = register_resource_handler< image_data >( new image_manager );
+	m_meshes        = register_resource_handler< data_channel_set >( new mesh_manager );
+	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_programs      = register_resource_handler< program >( new program_manager( context ) );
+	m_gpu_meshes    = register_resource_handler< gpu_mesh >( new gpu_mesh_manager( context, m_meshes ) );
+	m_mesh_datas    = register_resource_handler< mesh_data >( new mesh_data_manager( m_meshes ) );
+	m_gpu_materials = register_resource_handler< gpu_material >( new gpu_material_manager( context, m_materials, m_images ) );
+	m_models        = register_resource_handler< model >( new model_manager( this, m_binds, m_mesh_datas ) );
+}
+
+void default_resource_manager::initialize( lua::state* lua )
+{
+	m_lua = lua;
+
+	m_lua->register_enum( "INT_NONE",       static_cast<int>( interpolation::NONE ) );
+	m_lua->register_enum( "INT_LINEAR",     static_cast<int>( interpolation::LINEAR ) );
+	m_lua->register_enum( "INT_NORMALIZED", static_cast<int>( interpolation::NORMALIZED ) );
+	m_lua->register_enum( "INT_SPHERICAL",  static_cast<int>( interpolation::SPHERICAL ) );
+	m_lua->register_enum( "INT_QUADRATIC",  static_cast<int>( interpolation::QUADRATIC ) );
+	m_lua->register_enum( "INT_SQUADRATIC", static_cast<int>( interpolation::SQUADRATIC ) );
+
+	m_lua->register_enum( "EASING_BACK",    static_cast<int>( easing_type::BACK ) );
+	m_lua->register_enum( "EASING_BOUNCE",  static_cast<int>( easing_type::BOUNCE ) );
+	m_lua->register_enum( "EASING_CIRC",    static_cast<int>( easing_type::CIRC ) );
+	m_lua->register_enum( "EASING_CUBIC",   static_cast<int>( easing_type::CUBIC ) );
+	m_lua->register_enum( "EASING_ELASTIC", static_cast<int>( easing_type::ELASTIC ) );
+	m_lua->register_enum( "EASING_EXPO",    static_cast<int>( easing_type::EXPO ) );
+	m_lua->register_enum( "EASING_LINEAR",  static_cast<int>( easing_type::LINEAR ) );
+	m_lua->register_enum( "EASING_QUAD",    static_cast<int>( easing_type::QUAD ) );
+	m_lua->register_enum( "EASING_QUART",   static_cast<int>( easing_type::QUART ) );
+	m_lua->register_enum( "EASING_QUINT",   static_cast<int>( easing_type::QUINT ) );
+	m_lua->register_enum( "EASING_SINE",    static_cast<int>( easing_type::SINE ) );
+
+	m_materials->initialize( lua );
+	m_programs->initialize( lua );
+	m_animators->initialize( lua );
+	m_models->initialize( lua );
+}
+
+void default_resource_manager::reload_data()
+{
+	m_materials->clear();
+	m_materials->load_all();
+	//	m_models->load_all();
+	//	m_programs->load_all();
+}
+
+void nv::default_resource_manager::add_path( const string_view& path )
+{
+	m_images->add_base_path( path );
+	m_mesh_datas->add_base_path( path );
+	m_programs->add_base_path( path );
+	m_animators->add_base_path( path );
+}
Index: trunk/src/engine/material_manager.cc
===================================================================
--- trunk/src/engine/material_manager.cc	(revision 507)
+++ trunk/src/engine/material_manager.cc	(revision 508)
@@ -39,7 +39,7 @@
 
 		// HACK
-		for ( uint32 i = 0; i < 8; ++i )
-			if ( result->textures[i].is_nil() )
-				result->textures[i] = m_default;
+ 		for ( uint32 i = 0; i < 5; ++i )
+ 			if ( result->textures[i].is_nil() )
+ 				result->textures[i] = m_default;
 
 		return add( id, result );
@@ -77,5 +77,5 @@
 				if ( i != TEX_EMISSIVE )
 					NV_LOG_ERROR( "Texture file not found! : ", m->paths[i] );
-				m->paths[i].clear();
+				//m->paths[i].clear();
 			}
 		}
Index: trunk/src/engine/mesh_manager.cc
===================================================================
--- trunk/src/engine/mesh_manager.cc	(revision 507)
+++ trunk/src/engine/mesh_manager.cc	(revision 508)
@@ -18,5 +18,5 @@
 		gm->va = m_context->create_vertex_array( &*lmesh, STATIC_DRAW );
 		gm->count = lmesh->get_channel_size( slot::INDEX );
-		gm->shader = lmesh->get_channel( slot::BONEINDEX ) != nullptr ? BONE : NORMAL;
+		gm->shader = lmesh->get_channel( slot::BONEINDEX ) != nullptr ? BONE : DIRECT;
 		return add( mesh.id(), gm );
 	}
Index: trunk/src/engine/model_manager.cc
===================================================================
--- trunk/src/engine/model_manager.cc	(revision 507)
+++ trunk/src/engine/model_manager.cc	(revision 508)
@@ -64,5 +64,5 @@
 		if ( table.is_number( "attach" ) )
 		{
-			attach_id = table.get_integer( "attach", -1 );
+			attach_id = sint16( table.get_integer( "attach", -1 ) );
 			//				parent_id = 0;
 		}
@@ -71,5 +71,5 @@
 			auto it = m->node_names.find( table.get_string_hash_64( "attach" ) );
 			if ( it != m->node_names.end() )
-				attach_id = it->second + 1;
+				attach_id = sint16( it->second + 1 );
 			int error; int hack;
 		}
Index: trunk/src/engine/renderer.cc
===================================================================
--- trunk/src/engine/renderer.cc	(revision 507)
+++ trunk/src/engine/renderer.cc	(revision 508)
@@ -6,2 +6,166 @@
 
 #include "nv/engine/renderer.hh"
+
+#include "nv/core/profiler.hh"
+
+using namespace nv;
+
+renderer::renderer( context* ctx )
+	: m_context( ctx )
+{
+	for ( auto& i : m_statistics ) i = 0;
+
+	struct qvtx
+	{
+		nv::vec2 position;
+		nv::vec2 texcoord;
+	};
+
+	qvtx quad[] = {
+		qvtx{ vec2( -1.0f,-1.0f ), vec2( 0.0f, 0.0f ) },
+		qvtx{ vec2( 1.0f,-1.0f ),  vec2( 1.0f, 0.0f ) },
+		qvtx{ vec2( 1.0f,1.0f ),   vec2( 1.0f, 1.0f ) },
+		qvtx{ vec2( 1.0f,1.0f ),   vec2( 1.0f, 1.0f ) },
+		qvtx{ vec2( -1.0f,1.0f ),  vec2( 0.0f, 1.0f ) },
+		qvtx{ vec2( -1.0f,-1.0f ), vec2( 0.0f, 0.0f ) },
+	};
+	m_quad = m_context->create_vertex_array( quad, 6, STATIC_DRAW );
+}
+
+void renderer::sort()
+{
+	stable_sort( m_elements.begin(), m_elements.end(), renderer_element_compare() );
+}
+
+
+void renderer::render( const scene_state& s, const render_pass& pass )
+{
+	scene_state ss( s );
+	m_context->bind( pass.fbuffer, FRAMEBUFFER );
+	m_context->set_draw_buffers( pass.output_count, pass.output );
+	m_context->set_viewport( ss.get_viewport() );
+
+	if ( pass.cstate.buffers != buffer_mask::NO_BUFFER )
+		m_context->clear( pass.cstate );
+
+	for ( uint32 i = 0; i < size( pass.binds ); ++i )
+		if ( pass.binds[i] )
+			m_context->bind( pass.binds[i], texture_slot( i ) );
+
+	resource_id current_mat;
+	mat4 model = ss.get_model();
+	for ( const renderer_element& element : m_elements )
+		if ( match( element.flags, pass ) )
+		{
+			ss.set_model( model * element.model.extract() );
+			if ( auto mesh = element.mesh.lock() )
+				if ( mesh->count > 0 )
+					if ( auto program = pass.programs[mesh->shader].lock() )
+					{
+						if ( element.mat.id() != current_mat )
+						{
+							if ( auto mat = element.mat.lock() )
+							{
+								for ( uint32 i = 0; i < 8; ++i )
+									if ( mat->textures[i] && !pass.binds[i] )
+										m_context->bind( mat->textures[i], nv::texture_slot( i ) );
+								current_mat = element.mat.id();
+							}
+						}
+
+						m_context->get_device()->set_opt_uniform( *program, "nv_bone_offset", element.bone_offset );
+						m_context->get_device()->set_opt_uniform( *program, "nv_flags", unsigned( *element.flags.data() ) );
+
+						m_context->apply_engine_uniforms( *program, ss );
+						m_context->draw( TRIANGLES, pass.rstate, *program, mesh->va, mesh->count );
+
+						m_statistics[TRIANGLE_COUNT] += mesh->count / 3;
+						m_statistics[DRAW_CALL]++;
+					}
+		}
+
+	m_context->bind( pass.fbuffer, FRAMEBUFFER );
+}
+
+void renderer::render( const scene_state& ss, const render_pass& pass, vertex_array va, uint32 va_count, uint32 program_idx )
+{
+	m_context->bind( pass.fbuffer, FRAMEBUFFER );
+	m_context->set_draw_buffers( pass.output_count, pass.output );
+	m_context->set_viewport( ss.get_viewport() );
+
+	if ( pass.cstate.buffers != buffer_mask::NO_BUFFER )
+		m_context->clear( pass.cstate );
+
+	for ( uint32 i = 0; i < size( pass.binds ); ++i )
+		if ( pass.binds[i] )
+			m_context->bind( pass.binds[i], texture_slot(i) );
+
+	if ( auto program = pass.programs[program_idx].lock() )
+	{
+		m_context->apply_engine_uniforms( *program, ss );
+		m_context->draw( TRIANGLES, pass.rstate, *program, va, va_count );
+		m_statistics[ TRIANGLE_COUNT ] += 2;
+		m_statistics[ DRAW_CALL ]++;
+	}
+
+	m_context->bind( pass.fbuffer, FRAMEBUFFER );
+}
+
+void nv::renderer::render_direct( const scene_state& s, const render_pass& pass, const nv::array_view< renderer_direct_element >& elements, uint32 program_idx )
+{
+	if ( m_elements.size() == 0 ) return;
+
+	scene_state ss( s );
+	m_context->bind( pass.fbuffer, FRAMEBUFFER );
+	m_context->set_draw_buffers( pass.output_count, pass.output );
+	m_context->set_viewport( ss.get_viewport() );
+
+	if ( pass.cstate.buffers != buffer_mask::NO_BUFFER )
+		m_context->clear( pass.cstate );
+
+	for ( uint32 i = 0; i < size( pass.binds ); ++i )
+		if ( pass.binds[i] )
+			m_context->bind( pass.binds[i], texture_slot( i ) );
+
+	resource_id current_mat;
+	resource_id current_prog;
+
+	mat4 model = ss.get_model();
+	for ( uint32 index = 0; index < elements.size(); ++index )
+	{
+		const renderer_direct_element& element = elements[index];
+		if ( match( element.flags, pass ) )
+		{
+			ss.set_model( model * element.model.extract() );
+			if ( element.count > 0 )
+			{
+				if ( auto program = element.programs[program_idx].lock() )
+				{
+					if ( element.material.id() != current_mat )
+					{
+						if ( auto mat = element.material.lock() )
+						{
+							for ( uint32 i = 0; i < 8; ++i )
+								if ( mat->textures[i] && !pass.binds[i] )
+									m_context->bind( mat->textures[i], nv::texture_slot( i ) );
+							current_mat = element.material.id();
+						}
+					}
+
+					m_context->get_device()->set_opt_uniform( *program, "nv_flags", unsigned( *element.flags.data() ) );
+					m_context->get_device()->set_opt_uniform( *program, "nv_index", unsigned( index ) );
+					m_context->apply_engine_uniforms( *program, ss );
+
+					if ( element.instances > 0 )
+						m_context->draw_instanced( TRIANGLES, pass.rstate, *program, element.instances, element.va, element.count, element.first );
+					else
+						m_context->draw( TRIANGLES, pass.rstate, *program, element.va, element.count, element.first );
+
+					m_statistics[TRIANGLE_COUNT] += ( element.count / 3 ) * max< uint32 >( element.instances, 1 );
+					m_statistics[DRAW_CALL]++;
+				}
+			}
+		}
+	}
+	m_context->bind( pass.fbuffer, FRAMEBUFFER );
+}
Index: trunk/src/engine/shadow.cc
===================================================================
--- trunk/src/engine/shadow.cc	(revision 508)
+++ trunk/src/engine/shadow.cc	(revision 508)
@@ -0,0 +1,47 @@
+// Copyright (C) 2016-2016 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+#include "nv/engine/shadow.hh"
+
+using namespace nv;
+
+void shadow_data::initialize( context* context, uint32 count, uint32 map_size, const render_pass& pass_base )
+{
+	m_map_size = map_size;
+	m_maps = context->create_texture( TEXTURE_2D_ARRAY, ivec3( map_size, map_size, count ), image_format( DEPTH24, UINT ), sampler( sampler::LINEAR, sampler::CLAMP_TO_EDGE ), nullptr );
+	m_passes.resize( count );
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		framebuffer shadow_fbuffer = context->create_framebuffer();
+		if ( m_color_maps )
+			context->attach( shadow_fbuffer, OUTPUT_0, m_color_maps, i );
+		context->attach( shadow_fbuffer, m_maps, i );
+		context->check( FRAMEBUFFER );
+		context->bind( framebuffer() );
+
+		m_passes[i] = pass_base;
+		m_passes[i].output[0] = OUTPUT_NONE;
+		m_passes[i].output_count = 1;
+		m_passes[i].fbuffer = shadow_fbuffer;
+	}
+}
+
+void nv::shadow_data::initialize( context* context, uint32 count, uint32 map_size, const render_pass& pass_base, const render_pass& color_base )
+{
+	m_color_maps = context->create_texture( TEXTURE_2D_ARRAY, ivec3( map_size, map_size, count ), image_format( RGBA16F, FLOAT ), sampler( sampler::LINEAR, sampler::CLAMP_TO_EDGE ), nullptr );
+
+	initialize( context, count, map_size, pass_base );
+
+	m_color_passes.resize( count );
+	for ( uint32 i = 0; i < count; ++i )
+	{
+		m_color_passes[i] = color_base;
+		m_color_passes[i].output[0] = OUTPUT_0;
+		m_color_passes[i].output_count = 1;
+		m_color_passes[i].fbuffer = m_passes[i].fbuffer;
+	}
+
+}
Index: trunk/src/io/c_stream.cc
===================================================================
--- trunk/src/io/c_stream.cc	(revision 507)
+++ trunk/src/io/c_stream.cc	(revision 508)
@@ -75,5 +75,5 @@
 	{
 		struct stat fstat; 
-		int result = stat(m_file_name, &fstat ); 
+		int result = stat(m_file_name.data(), &fstat ); 
 		if ( result != 0 ) 
 		{
