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;
+	}
+
+}
