// Copyright (C) 2011 Kornel Kisielewicz // This file is part of NV Libraries. // For conditions of distribution and use, see copyright notice in nv.hh #include "nv/gfx/keyframed_mesh.hh" #include #include #include "nv/interface/context.hh" #include "nv/interface/device.hh" #include "nv/logging.hh" using namespace nv; keyframed_mesh::keyframed_mesh( context* a_context, keyframed_mesh_data* a_data, program* a_program ) : m_context( a_context ) , m_data( a_data ) , m_program( a_program ) , m_va( nullptr ) , m_loc_next_position( 0 ) , m_loc_last_position( 0 ) , m_loc_next_normal( 0 ) , m_loc_last_normal( 0 ) , m_last_frame( 0 ) , m_next_frame( 0 ) , m_gpu_last_frame( 0xFFFFFFFF ) , m_gpu_next_frame( 0xFFFFFFFF ) , m_interpolation( 0.0f ) , m_start_frame( false ) , m_stop_frame( false ) , m_looping( false ) , m_active( false ) { m_va = m_context->get_device()->create_vertex_array(); nv::vertex_buffer* vb; m_loc_next_position = m_program->get_attribute( "next_position" )->get_location(); m_loc_last_position = m_program->get_attribute( "last_position" )->get_location(); m_loc_next_normal = m_program->get_attribute( "next_normal" )->get_location(); m_loc_last_normal = m_program->get_attribute( "last_normal" )->get_location(); vb = m_context->get_device()->create_vertex_buffer( nv::STATIC_DRAW, m_data->get_vertex_count() * sizeof( nv::vec3 ) * m_data->get_frame_count(), (void*)m_data->get_positions().data() ); m_va->add_vertex_buffer( m_loc_next_position, vb, nv::FLOAT, 3, 0, 0, false ); m_va->add_vertex_buffer( m_loc_last_position, vb, nv::FLOAT, 3 ); vb = m_context->get_device()->create_vertex_buffer( nv::STATIC_DRAW, m_data->get_vertex_count() * sizeof( nv::vec3 ) * m_data->get_frame_count(), (void*)m_data->get_normals().data() ); m_va->add_vertex_buffer( m_loc_next_normal, vb, nv::FLOAT, 3, 0, 0, false ); m_va->add_vertex_buffer( m_loc_last_normal, vb, nv::FLOAT, 3 ); vb = m_context->get_device()->create_vertex_buffer( nv::STATIC_DRAW, m_data->get_vertex_count() * sizeof( nv::vec2 ), (void*)m_data->get_texcoords().data() ); m_va->add_vertex_buffer( m_program->get_attribute( "texcoord" )->get_location(), vb, nv::FLOAT, 2 ); nv::index_buffer* ib = m_context->get_device()->create_index_buffer( nv::STATIC_DRAW, m_data->get_index_count() * sizeof( nv::uint16 ), (void*)m_data->get_indices().data() ); m_va->set_index_buffer( ib, nv::USHORT, true ); } mat4 keyframed_mesh::get_tag( const std::string& tag ) const { const std::vector< nv::mat4 >& transforms = m_data->get_tag_map().at( tag ); return glm::interpolate( transforms[ m_last_frame ], transforms[ m_next_frame ], m_interpolation ); } uint32 keyframed_mesh::get_max_frames() const { return m_data->get_frame_count(); } void keyframed_mesh::set_frame( uint32 frame ) { m_last_frame = frame; m_next_frame = frame; m_interpolation = 0.0f; m_active = false; } void keyframed_mesh::setup_animation( uint32 start, uint32 stop, uint32 fps, bool loop ) { m_start_frame = start; m_stop_frame = stop; m_looping = loop; m_fps = fps; m_active = ( stop > start ); m_last_frame = start; m_next_frame = (stop > start ? start + 1 : start ); m_interpolation = 0.0f; m_time = 0; } void keyframed_mesh::update( uint32 ms ) { if ( m_active ) { m_time += ms; uint32 f_diff = (m_stop_frame - m_start_frame); float f_time = 1000 / (float)m_fps; float f_max = ( m_looping ? ( f_diff + 1 ) : f_diff ) * f_time; float f_pos = m_time / f_time; m_last_frame = (uint32)glm::floor( f_pos ) + m_start_frame; m_next_frame = m_last_frame + 1; if ( m_next_frame > m_stop_frame ) { m_next_frame = m_start_frame; } if ( m_time >= f_max ) { if ( m_looping ) { uint32 left = m_time - f_max; m_time = 0; update( left ); } else { m_active = false; m_last_frame = m_stop_frame; m_next_frame = m_stop_frame; } } m_interpolation = f_pos - glm::floor( f_pos ); } } void nv::keyframed_mesh::draw( render_state& rstate ) { nv::uint32 vtx_count = m_data->get_vertex_count(); if ( m_gpu_last_frame != m_last_frame ) { m_va->update_vertex_buffer( m_loc_last_position, m_last_frame * vtx_count * sizeof( nv::vec3 ) ); m_va->update_vertex_buffer( m_loc_last_normal, m_last_frame * vtx_count * sizeof( nv::vec3 ) ); m_gpu_last_frame = m_last_frame; } if ( m_gpu_next_frame != m_next_frame ) { m_va->update_vertex_buffer( m_loc_next_position, m_next_frame * vtx_count * sizeof( nv::vec3 ) ); m_va->update_vertex_buffer( m_loc_next_normal, m_next_frame * vtx_count * sizeof( nv::vec3 ) ); m_gpu_next_frame = m_next_frame; } m_program->set_uniform( "interpolate", m_interpolation ); m_context->draw( nv::TRIANGLES, rstate, m_program, m_va, m_data->get_index_count() ); } nv::keyframed_mesh::~keyframed_mesh() { delete m_va; }