// 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/skeleton_instance.hh" #include "nv/core/profiler.hh" void nv::skeleton_binding::prepare( const mesh_nodes_data* node_data, const data_node_list& bone_data ) { if ( m_indices.empty() ) { // TODO: either fixed size struct or static allocator hash_store< shash64, uint16 > bone_names; m_indices.resize( node_data->size() ); for ( nv::uint16 bi = 0; bi < bone_data.size(); ++bi ) bone_names[bone_data[bi].name] = bi; for ( uint32 n = 0; n < node_data->size(); ++n ) { sint16 bone_id = -1; auto bi = bone_names.find( node_data->get_info( n ).name ); if ( bi != bone_names.end() ) { bone_id = sint16( bi->second ); } m_indices[n] = bone_id; } m_bone_count = bone_data.size(); } if ( m_key.size() == 0 ) { for ( uint32 n = 0; n < node_data->size(); ++n ) if ( ( *node_data )[n]->size() > 0 ) { m_key = ( *node_data )[n]->get_interpolation_key(); break; } } } void nv::skeleton_instance::assign( const skeleton_transforms& skeleton, const bone_transforms& bones ) { if ( bones.size() != m_matrix.size() ) m_matrix.resize( bones.size() ); const transform* transforms = skeleton.transforms(); for ( uint32 n = 0; n < skeleton.size(); ++n ) { transform tr( bones.m_offsets[n] ); tr.set_orientation( normalize( tr.get_orientation() ) ); m_matrix[n] = ( transforms[n] * tr ).extract(); // m_matrix[n] = transforms[n].extract() * bones.m_offsets[n]; } } void nv::skeleton_instance::assign( const bone_transforms& bones ) { if ( bones.size() != m_matrix.size() ) m_matrix.resize( bones.size() ); } void nv::skeleton_transforms::assign( const data_node_list* node_data ) { NV_ASSERT( node_data, "!!!" ); if ( m_transforms.size() != node_data->size() ) m_transforms.resize( node_data->size() ); for ( uint32 n = 0; n < node_data->size(); ++n ) { const data_node_info& info = (*node_data)[ n ]; m_transforms[n] = transform( info.transform ); } } void nv::skeleton_transforms::interpolate_linear( const skeleton_transforms& a, const skeleton_transforms& b, float t ) { NV_ASSERT( a.size() == b.size(), "!!!" ); if ( m_transforms.size() != a.size() ) m_transforms.resize( a.size() ); for ( uint32 n = 0; n < a.size(); ++n ) { m_transforms[n] = transform( math::mix( a.m_transforms[n].get_position(), b.m_transforms[n].get_position(), t ), math::lerp( a.m_transforms[n].get_orientation(), b.m_transforms[n].get_orientation(), t ) ); } if ( m_transforms.size() > 0 ) m_transforms[0] = nv::interpolate( a.m_transforms[0], b.m_transforms[0], t ); } void nv::skeleton_transforms::interpolate_nlerp( const skeleton_transforms& a, const skeleton_transforms& b, float t ) { NV_ASSERT( a.size() == b.size(), "!!!" ); if ( m_transforms.size() != a.size() ) m_transforms.resize( a.size() ); for ( uint32 n = 0; n < a.size(); ++n ) { m_transforms[n] = transform( math::mix( a.m_transforms[n].get_position(), b.m_transforms[n].get_position(), t ), math::nlerp( a.m_transforms[n].get_orientation(), b.m_transforms[n].get_orientation(), t ) ); } if ( m_transforms.size() > 0 ) m_transforms[0] = nv::interpolate( a.m_transforms[0], b.m_transforms[0], t ); } void nv::skeleton_transforms::interpolate_slerp( const skeleton_transforms& a, const skeleton_transforms& b, float t ) { NV_ASSERT( a.size() == b.size(), "!!!" ); if ( m_transforms.size() != a.size() ) m_transforms.resize( a.size() ); for ( uint32 n = 0; n < a.size(); ++n ) { m_transforms[n] = nv::interpolate( a.m_transforms[n], b.m_transforms[n], t ); } } void nv::skeleton_transforms::interpolate4( const skeleton_transforms& s1, const skeleton_transforms& v1, const skeleton_transforms& v2, const skeleton_transforms& s2, float t ) { NV_ASSERT( s1.size() == s2.size(), "!!!" ); NV_ASSERT( v1.size() == v2.size(), "!!!" ); NV_ASSERT( s1.size() == v1.size(), "!!!" ); if ( m_transforms.size() != s1.size() ) m_transforms.resize( s1.size() ); float interp_squared = t*t; float interp_cubed = interp_squared*t; float weights[4]; weights[0] = 0.5f * ( -interp_cubed + 2.0f * interp_squared - t ); weights[1] = 0.5f * ( 3.0f * interp_cubed - 5.0f * interp_squared + 2.0f ); weights[2] = 0.5f * ( -3.0f * interp_cubed + 4.0f * interp_squared + t ); weights[3] = 0.5f * ( interp_cubed - interp_squared ); for ( uint32 n = 0; n < s1.size(); ++n ) { quat qs1 = s1.m_transforms[n].get_orientation(); quat qs2 = s2.m_transforms[n].get_orientation(); quat qv1 = v1.m_transforms[n].get_orientation(); quat qv2 = v2.m_transforms[n].get_orientation(); float a = dot( qv1, qv2 ) > 0.0f ? 1.0f : -1.0f; quat qr = weights[0] * qs1 + weights[1] * (a * qv1 ) + weights[2] * qv2 + weights[3] * qs2; qr = normalize( qr ); if ( n == 0 ) qr = nv::math::slerp( v1.m_transforms[n].get_orientation(), v2.m_transforms[n].get_orientation(), t ); m_transforms[n] = transform( weights[0] * s1.m_transforms[n].get_position() + weights[1] * v1.m_transforms[n].get_position() + weights[2] * v2.m_transforms[n].get_position() + weights[3] * s2.m_transforms[n].get_position(), qr ); } } void nv::skeleton_transforms::interpolate_squad( const skeleton_transforms& s1, const skeleton_transforms& v1, const skeleton_transforms& v2, const skeleton_transforms& s2, float t ) { NV_ASSERT( s1.size() == s2.size(), "!!!" ); NV_ASSERT( v1.size() == v2.size(), "!!!" ); NV_ASSERT( s1.size() == v1.size(), "!!!" ); if ( m_transforms.size() != s1.size() ) m_transforms.resize( s1.size() ); for ( uint32 n = 0; n < s1.size(); ++n ) { nv::quat ss1 = s1.m_transforms[n].get_orientation(); nv::quat ss2 = s2.m_transforms[n].get_orientation(); nv::quat q = normalize( nv::math::squad( v1.m_transforms[n].get_orientation(), v2.m_transforms[n].get_orientation(), nv::math::intermediate( ss1, v1.m_transforms[n].get_orientation(), v2.m_transforms[n].get_orientation() ), nv::math::intermediate( v1.m_transforms[n].get_orientation(), v2.m_transforms[n].get_orientation(), ss2 ), t ) ); if ( n == 0 ) q = nv::math::slerp( v1.m_transforms[n].get_orientation(), v2.m_transforms[n].get_orientation(), t ); m_transforms[n] = transform( mix( v1.m_transforms[n].get_position(), v2.m_transforms[n].get_position(), t ), q ); } } void nv::skeleton_transforms::assign( const skeleton_transforms& other ) { m_transforms.assign( other.m_transforms ); } void nv::skeleton_transforms::animate_local( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame ) { if ( m_transforms.size() != binding.skeleton_size() ) m_transforms.resize( binding.skeleton_size() ); for ( uint32 n = 0; n < node_data->size(); ++n ) { const data_channel_set* node = ( *node_data )[n]; sint16 bone_id = binding.m_indices[n]; if ( bone_id >= 0 ) { if ( node->size() > 0 ) { m_transforms[bone_id] = raw_channel_interpolator( node, binding.m_key ).get< transform >( frame ); } int confirm_that_not_needed; // else // m_transforms[bone_id] = transform( node->get_transform() ); } } } void nv::skeleton_transforms::blend_local( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame, float blend ) { if ( m_transforms.size() != binding.skeleton_size() ) m_transforms.resize( binding.skeleton_size() ); for ( uint32 n = 0; n < node_data->size(); ++n ) { const data_channel_set* node = ( *node_data )[n]; sint16 bone_id = binding.m_indices[n]; if ( bone_id >= 0 ) { transform tr = node->size() > 0 ? raw_channel_interpolator( node, binding.m_key ).get< transform >( frame ) : transform( /*node->get_transform()*/ ); int confirm_that_not_needed; m_transforms[bone_id] = nv::interpolate( m_transforms[bone_id], tr, blend ); } } } void nv::skeleton_transforms::delocalize_rec( const data_node_tree& node_data, const skeleton_binding& binding, uint32 id, const transform& parent ) { sint16 bone_id = binding.m_indices[id]; transform global_mat = parent; if ( bone_id >= 0 ) { global_mat *= m_transforms[bone_id]; m_transforms[bone_id] = global_mat; } for ( auto child : node_data.children( id ) ) { delocalize_rec( node_data, binding, child, global_mat ); } } void nv::skeleton_transforms::animate_rec( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame, uint32 id, const transform& parent, bool local ) { const data_channel_set* node = ( *node_data )[id]; transform node_mat; if ( node->size() > 0 ) node_mat = raw_channel_interpolator( node, binding.m_key ).get< transform >( frame ); int confirm_that_not_needed; // else // node_mat = transform( node->get_transform() ); sint16 bone_id = binding.m_indices[id]; transform global_mat = parent * node_mat; if ( bone_id >= 0 ) { m_transforms[bone_id] = local ? node_mat : global_mat; } for ( auto child : node_data->children( id ) ) { animate_rec( node_data, binding, frame, child, global_mat, local ); } } void nv::skeleton_transforms::blend_rec( const mesh_nodes_data* node_data, const skeleton_binding& binding, float frame, uint32 id, const transform& parent, bool local, float blend ) { const data_channel_set* node = ( *node_data )[id]; int confirm_that_not_needed; transform node_mat/*( node->get_transform() )*/; if ( node->size() > 0 ) { raw_channel_interpolator interpolator( node, binding.m_key ); node_mat = interpolator.get< transform >( frame ); } sint16 bone_id = binding.m_indices[id]; transform global_mat = parent * node_mat; if ( bone_id >= 0 ) { m_transforms[bone_id] = nv::interpolate( m_transforms[bone_id], local ? node_mat : global_mat, blend ); } for ( auto child : node_data->children( id ) ) { blend_rec( node_data, binding, frame, child, global_mat, local, blend ); } } void nv::bone_transforms::prepare( const data_node_list& bone_data ) { m_offsets.resize( bone_data.size() ); for ( nv::uint16 bi = 0; bi < bone_data.size(); ++bi ) m_offsets[bi] = bone_data[bi].transform; }