// 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/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 ); render_state rs( pass.rstate ); 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 ( rs.blending != element.blending ) rs.blending = element.blending; if ( element.instances > 0 ) m_context->draw_instanced( TRIANGLES, rs, *program, element.instances, element.va, element.count, element.first ); else m_context->draw( TRIANGLES, rs, *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 ); }