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