// Copyright (C) 2011-2015 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/gfx/keyframed_mesh.hh" #include "nv/interface/context.hh" #include "nv/interface/device.hh" #include "nv/core/logging.hh" using namespace nv; nv::keyframed_mesh::keyframed_mesh( context* a_context, const mesh_data* a_data, const mesh_nodes_data* a_tag_map ) : animated_mesh() , m_context( a_context ) , m_mesh_data( a_data ) , m_tag_map( a_tag_map ) , m_last_frame( 0 ) , m_next_frame( 0 ) , m_interpolation( 0.0f ) , m_active( false ) { m_index_count = m_mesh_data->get_channel_size( slot::INDEX ); m_vertex_count = m_mesh_data->get_channel_size(); uint32 pos_size = m_mesh_data->get_channel_size(); if ( pos_size == 0 ) { pos_size = m_mesh_data->get_channel_size(); m_has_tangent = false; m_vsize = sizeof( vertex_pn ); } else { m_has_tangent = true; m_vsize = sizeof( vertex_pnt ); } m_frame_count = pos_size / m_vertex_count; m_pbuffer = buffer(); } nv::size_t keyframed_mesh::get_max_frames() const { return m_frame_count; } transform keyframed_mesh::get_node_transform( uint32 node_id ) const { if ( !m_tag_map ) return transform(); NV_ASSERT( node_id < m_tag_map->get_count(), "TAGMAP FAIL" ); const key_channel_set* data = m_tag_map->get_node( node_id )->data; NV_ASSERT( data, "TAG FAIL" ); transform last = data->get_raw_transform( m_last_frame ); transform next = data->get_raw_transform( m_next_frame ); return interpolate( last, next, m_interpolation ); } mat4 keyframed_mesh::get_node_matrix( uint32 node_id ) const { return get_node_transform( node_id ).extract(); } void nv::keyframed_mesh::set_frame( uint32 frame ) { m_last_frame = frame; m_next_frame = frame; m_active = false; m_interpolation = 0.0f; } void nv::keyframed_mesh::update_animation( animation_entry* anim, uint32 a_anim_time ) { if ( m_active ) { float tick_time = ( static_cast( a_anim_time ) * 0.001f ) * anim->get_frame_rate(); float duration = anim->is_looping() ? anim->get_duration() + 1.0f : anim->get_duration(); if ( tick_time >= duration ) { if ( anim->is_looping() ) { tick_time = fmodf( tick_time, duration ); } else { m_active = false; m_last_frame = static_cast( anim->get_end() ); m_next_frame = m_last_frame; m_interpolation = 0.0f; return; } } m_last_frame = static_cast( glm::floor( tick_time ) + anim->get_start() ); m_next_frame = m_last_frame + 1; if ( m_next_frame > static_cast( anim->get_end() ) ) m_next_frame = static_cast( anim->get_start() ); m_interpolation = tick_time - glm::floor( tick_time ); } } void nv::keyframed_mesh::update( program a_program ) { m_context->get_device()->set_opt_uniform( a_program, "nv_interpolate", m_interpolation ); } nv::keyframed_mesh::~keyframed_mesh() { m_context->release( m_va ); } void nv::keyframed_mesh::run_animation( animation_entry* a_anim ) { if ( a_anim ) { m_active = true; m_last_frame = 0; m_next_frame = 0; m_interpolation = 0.0f; } else { m_active = false; } } nv::keyframed_mesh_gpu::keyframed_mesh_gpu( context* a_context, const mesh_data* a_data, const mesh_nodes_data* a_tag_map ) : keyframed_mesh( a_context, a_data, a_tag_map ) , m_loc_next_position( -1 ) , m_loc_next_normal( -1 ) , m_loc_next_tangent( -1 ) , m_gpu_last_frame( 0xFFFFFFFF ) , m_gpu_next_frame( 0xFFFFFFFF ) { m_va = a_context->create_vertex_array( a_data, STATIC_DRAW ); m_pbuffer = a_context->find_buffer( m_va, slot::POSITION ); } void nv::keyframed_mesh_gpu::update_animation( animation_entry* anim, uint32 a_anim_time ) { keyframed_mesh::update_animation( anim, a_anim_time ); if ( m_loc_next_position == -1 ) return; if ( m_gpu_last_frame != m_last_frame ) { uint32 base_offset = m_last_frame * m_vertex_count * m_vsize; m_context->update_attribute_offset( m_va, slot::POSITION, base_offset ); m_context->update_attribute_offset( m_va, slot::NORMAL, base_offset + sizeof( vec3 ) ); if ( m_has_tangent && m_loc_next_tangent != -1 ) { m_context->update_attribute_offset( m_va, slot::TANGENT, base_offset + 2*sizeof( vec3 ) ); } m_gpu_last_frame = m_last_frame; } if ( m_loc_next_position != -1 && m_gpu_next_frame != m_next_frame ) { uint32 base_offset = m_next_frame * m_vertex_count * m_vsize; m_context->update_attribute_offset( m_va, static_cast( m_loc_next_position ), base_offset ); m_context->update_attribute_offset( m_va, static_cast( m_loc_next_normal ), base_offset + sizeof( vec3 ) ); if ( m_has_tangent && m_loc_next_tangent != -1 ) { m_context->update_attribute_offset( m_va, static_cast( m_loc_next_tangent ), base_offset + 2*sizeof( vec3 ) ); } m_gpu_next_frame = m_next_frame; } } void nv::keyframed_mesh_gpu::update( program a_program ) { if ( m_loc_next_position == -1 ) { device* dev = m_context->get_device(); m_loc_next_position = dev->get_attribute_location( a_program, "nv_next_position" ); m_loc_next_normal = dev->get_attribute_location( a_program, "nv_next_normal" ); if ( m_has_tangent ) m_loc_next_tangent = dev->get_attribute_location( a_program, "nv_next_tangent" ); m_context->add_vertex_buffer( m_va, static_cast( m_loc_next_position ), m_pbuffer, FLOAT, 3, 0, m_vsize, false ); m_context->add_vertex_buffer( m_va, static_cast( m_loc_next_normal ), m_pbuffer, FLOAT, 3, sizeof( vec3 ), m_vsize, false ); if ( m_has_tangent ) m_context->add_vertex_buffer( m_va, static_cast( m_loc_next_tangent ), m_pbuffer, FLOAT, 4, 2*sizeof( vec3 ), m_vsize, false ); } keyframed_mesh::update( a_program ); } nv::keyframed_mesh_cpu::keyframed_mesh_cpu( context* a_context, const mesh_data* a_data, const mesh_nodes_data* a_tag_map ) : keyframed_mesh( a_context, a_data, a_tag_map ) { const raw_data_channel* vchannel = m_has_tangent ? a_data->get_channel< vertex_pnt >() : a_data->get_channel< vertex_pn >(); m_va = m_context->create_vertex_array(); m_pbuffer = m_context->get_device()->create_buffer( VERTEX_BUFFER, STATIC_DRAW, m_vertex_count * m_vsize, vchannel->raw_data() ); m_context->add_vertex_buffers( m_va, m_pbuffer, vchannel->descriptor() ); buffer vb = m_context->get_device()->create_buffer( VERTEX_BUFFER, STATIC_DRAW, m_vertex_count * sizeof( vec2 ), m_mesh_data->get_channel_data() ); m_context->add_vertex_buffers( m_va, vb, m_mesh_data->get_channel()->descriptor() ); const raw_data_channel* index_channel = m_mesh_data->get_channel( slot::INDEX ); buffer ib = m_context->get_device()->create_buffer( INDEX_BUFFER, STATIC_DRAW, index_channel->raw_size(), index_channel->raw_data() ); m_context->set_index_buffer( m_va, ib, index_channel->descriptor()[0].etype, true ); m_data = new uint8[ m_vertex_count * m_vsize ]; } void nv::keyframed_mesh_cpu::update_animation( animation_entry* anim, uint32 a_anim_time ) { keyframed_mesh::update_animation( anim, a_anim_time ); // TODO: this could be done generic for any data if ( m_has_tangent ) { const vertex_pnt* data = m_mesh_data->get_channel_data(); const vertex_pnt* prev = data + m_vertex_count * m_last_frame; const vertex_pnt* next = data + m_vertex_count * m_next_frame; vertex_pnt* vtx = reinterpret_cast( m_data ); for ( size_t i = 0; i < m_vertex_count; ++i ) { vtx[i].position = glm::mix( prev[i].position, next[i].position, m_interpolation ); vtx[i].normal = glm::mix( prev[i].normal, next[i].normal, m_interpolation ); vtx[i].tangent = glm::mix( prev[i].tangent, next[i].tangent, m_interpolation ); } } else { const vertex_pn* data = m_mesh_data->get_channel_data(); const vertex_pn* prev = data + m_vertex_count * m_last_frame; const vertex_pn* next = data + m_vertex_count * m_next_frame; vertex_pn* vtx = reinterpret_cast( m_data ); for ( size_t i = 0; i < m_vertex_count; ++i ) { vtx[i].position = glm::mix( prev[i].position, next[i].position, m_interpolation ); vtx[i].normal = glm::mix( prev[i].normal, next[i].normal, m_interpolation ); } } m_context->update( m_pbuffer, m_data, 0, m_vertex_count * m_vsize ); } nv::keyframed_mesh_cpu::~keyframed_mesh_cpu() { delete[] m_data; }