// 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::assign( const skeleton_binding& other ) { m_indices.assign( other.m_indices ); m_key = other.m_key; m_bone_count = other.m_bone_count; } 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_binding::prepare( const data_node_list& pose_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( pose_data.size() ); for ( nv::uint16 bi = 0; bi < bone_data.size(); ++bi ) bone_names[bone_data[bi].name] = bi; for ( uint32 n = 0; n < pose_data.size(); ++n ) { sint16 bone_id = -1; auto bi = bone_names.find( pose_data[ n ].name ); if ( bi != bone_names.end() ) { bone_id = sint16( bi->second ); } m_indices[n] = bone_id; } m_bone_count = bone_data.size(); } } void nv::skeleton_instance::assign( const skeleton_transforms& skeleton, const skeleton_binding& binding, const bone_transforms& bones ) { if ( bones.size() != m_matrix.size() ) m_matrix.resize( bones.size() ); const transform* transforms = skeleton.xforms(); for ( uint32 n = 0; n < skeleton.size(); ++n ) { sint16 bone_id = binding.m_indices[n]; if ( bone_id >= 0 ) { int too_complex; transform tr( bones.m_offsets[bone_id] ); tr.set_orientation( normalize( tr.get_orientation() ) ); m_matrix[bone_id] = ( transforms[n] * tr ).extract(); } } } 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.xforms(); 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, interpolation::SPHERICAL ); } 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 ) ); } } 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, interpolation::SPHERICAL ); } } void nv::skeleton_transforms::blend_slerp( const skeleton_transforms& a, const skeleton_transforms& b, float t, float blend ) { 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 ) { transform tr = nv::interpolate( a.m_transforms[n], b.m_transforms[n], t, interpolation::SPHERICAL ); m_transforms[n] = nv::interpolate( m_transforms[n], tr, blend, interpolation::SPHERICAL ); } } 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 ); 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 sv1 = v1.m_transforms[n].get_orientation(); nv::quat sv2 = v2.m_transforms[n].get_orientation(); nv::quat q = normalize( nv::math::squad( sv1, sv2, nv::math::intermediate( ss1, sv1, sv2 ), nv::math::intermediate( sv1, sv2, ss2 ), 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::blend_local( const mesh_nodes_data* node_data, float frame, float blend ) // { // if ( m_transforms.size() != node_data->size() ) // m_transforms.resize( node_data->size() ); // for ( uint32 n = 0; n < node_data->size(); ++n ) // { // const data_channel_set* node = ( *node_data )[n]; // int inefficient_store_key; // // transform tr = node->size() > 0 ? raw_channel_interpolator( node ).get< transform >( frame ) : transform( /*node->get_transform()*/ ); int confirm_that_not_needed; // m_transforms[n] = nv::interpolate( m_transforms[n], tr, blend, interpolation::SPHERICAL ); // } // } // // void nv::skeleton_transforms::animate_local( const mesh_nodes_data* node_data, float frame ) // { // if ( m_transforms.size() != node_data->size() ) // m_transforms.resize( node_data->size() ); // for ( uint32 n = 0; n < node_data->size(); ++n ) // { // const data_channel_set* node = ( *node_data )[n]; // if ( node->size() > 0 ) // { // int inefficient_store_key; // m_transforms[n] = raw_channel_interpolator( node ).get< transform >( frame ); // } // } // } void nv::skeleton_transforms::delocalize_rec( const data_node_tree& node_data, uint32 id, const transform& parent ) { transform global_mat = parent; global_mat *= m_transforms[id]; m_transforms[id] = global_mat; for ( auto child : node_data.children( id ) ) { delocalize_rec( node_data, child, global_mat ); } } // void nv::skeleton_transforms::blend_rec( const mesh_nodes_data* node_data, 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 ) // { // int inefficient_store_key; // // raw_channel_interpolator interpolator( node ); // node_mat = interpolator.get< transform >( frame ); // } // transform global_mat = parent * node_mat; // m_transforms[id] = nv::interpolate( m_transforms[id], local ? node_mat : global_mat, blend, interpolation::SPHERICAL ); // for ( auto child : node_data->children( id ) ) // { // blend_rec( node_data, frame, child, global_mat, local, blend ); // } // // } // // void nv::skeleton_transforms::animate_rec( const mesh_nodes_data* node_data, float frame, uint32 id, const transform& parent, bool local ) // { // const data_channel_set* node = ( *node_data )[id]; // transform node_mat; // int inefficient_store_key; // // if ( node->size() > 0 ) // node_mat = raw_channel_interpolator( node ).get< transform >( frame ); // int confirm_that_not_needed; // // else // // node_mat = transform( node->get_transform() ); // transform global_mat = parent * node_mat; // m_transforms[id] = local ? node_mat : global_mat; // for ( auto child : node_data->children( id ) ) // { // animate_rec( node_data, frame, child, global_mat, local ); // } // // } 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; }