[395] | 1 | // Copyright (C) 2011-2015 ChaosForge Ltd
|
---|
| 2 | // http://chaosforge.org/
|
---|
| 3 | //
|
---|
| 4 | // This file is part of Nova libraries.
|
---|
| 5 | // For conditions of distribution and use, see copying.txt file in root folder.
|
---|
[227] | 6 |
|
---|
| 7 | #include "nv/gfx/skeletal_mesh.hh"
|
---|
| 8 |
|
---|
| 9 | #include "nv/interface/context.hh"
|
---|
| 10 | #include "nv/interface/device.hh"
|
---|
[392] | 11 | #include "nv/stl/unordered_map.hh"
|
---|
[227] | 12 |
|
---|
[430] | 13 | nv::skeletal_mesh_cpu::skeletal_mesh_cpu( context* a_context, const data_channel_set* a_mesh_data, const mesh_nodes_data* bones )
|
---|
[302] | 14 | : skeletal_mesh( a_context )
|
---|
[227] | 15 | {
|
---|
[412] | 16 | const raw_data_channel* pnt_chan = a_mesh_data->get_channel<md5_vtx_pnt>();
|
---|
| 17 | const raw_data_channel* pntiw_chan = a_mesh_data->get_channel<md5_vtx_pntiw>();
|
---|
| 18 |
|
---|
[415] | 19 | m_pntdata.assign( pnt_chan->data_cast< md5_vtx_pnt >(), pnt_chan->size() );
|
---|
[428] | 20 | m_bone_offset.resize( bones->size() );
|
---|
| 21 | m_transform.resize( bones->size() );
|
---|
[289] | 22 |
|
---|
[428] | 23 | for ( uint32 i = 0; i < bones->size(); ++i )
|
---|
[287] | 24 | {
|
---|
[427] | 25 | m_bone_offset[i] = transform( (*bones)[i]->get_transform() );
|
---|
[287] | 26 | }
|
---|
[412] | 27 |
|
---|
[287] | 28 | m_vtx_data = a_mesh_data->get_channel_data<md5_vtx_pntiw>();
|
---|
[416] | 29 | m_indices = a_mesh_data->get_channel_size( slot::INDEX );
|
---|
[412] | 30 | m_va = a_context->create_vertex_array();
|
---|
| 31 |
|
---|
[416] | 32 | //array_view< raw_data_channel* > channels = a_mesh_data->get_raw_channels();
|
---|
[418] | 33 | for ( auto& channel : *a_mesh_data )
|
---|
[412] | 34 | {
|
---|
[416] | 35 | //const raw_data_channel* channel = channels[ch];
|
---|
[418] | 36 | if ( channel.size() > 0 && &channel != pntiw_chan )
|
---|
[412] | 37 | {
|
---|
[418] | 38 | const data_descriptor& desc = channel.descriptor();
|
---|
[412] | 39 | if ( desc[0].vslot == slot::INDEX )
|
---|
| 40 | {
|
---|
[418] | 41 | buffer b = a_context->get_device()->create_buffer( INDEX_BUFFER, STREAM_DRAW, channel.raw_size(), channel.raw_data() );
|
---|
[412] | 42 | a_context->set_index_buffer( m_va, b, desc[0].etype, true );
|
---|
| 43 | }
|
---|
| 44 | else
|
---|
| 45 | {
|
---|
[418] | 46 | buffer b = a_context->get_device()->create_buffer( VERTEX_BUFFER, STREAM_DRAW, channel.raw_size(), channel.raw_data() );
|
---|
[417] | 47 | a_context->add_vertex_buffers( m_va, b, desc );
|
---|
[412] | 48 | }
|
---|
| 49 | }
|
---|
| 50 | }
|
---|
| 51 |
|
---|
[313] | 52 | m_pbuffer = a_context->find_buffer( m_va, slot::POSITION );
|
---|
[239] | 53 | }
|
---|
[227] | 54 |
|
---|
[296] | 55 | void nv::skeletal_mesh_cpu::update_animation( animation_entry* a_anim, uint32 a_anim_time )
|
---|
[227] | 56 | {
|
---|
[283] | 57 | if ( a_anim )
|
---|
[241] | 58 | {
|
---|
[406] | 59 | skeletal_animation_entry_cpu * anim = static_cast<skeletal_animation_entry_cpu*>( a_anim );
|
---|
| 60 | anim->update_skeleton( m_transform.data(), static_cast<float>( a_anim_time ) );
|
---|
[287] | 61 | {
|
---|
| 62 | size_t skeleton_size = m_bone_offset.size();
|
---|
| 63 | size_t vertex_count = m_pntdata.size();
|
---|
| 64 | m_pos_offset.resize( skeleton_size );
|
---|
| 65 | for ( unsigned int i = 0; i < skeleton_size; ++i )
|
---|
| 66 | {
|
---|
| 67 | m_pos_offset[i] = m_transform[i] * m_bone_offset[i];
|
---|
| 68 | }
|
---|
| 69 |
|
---|
[383] | 70 | fill( m_pntdata.raw_data(), m_pntdata.raw_data() + m_pntdata.raw_size(), 0 );
|
---|
[287] | 71 | for ( unsigned int i = 0; i < vertex_count; ++i )
|
---|
| 72 | {
|
---|
| 73 | const md5_vtx_pntiw& vert = m_vtx_data[i];
|
---|
| 74 |
|
---|
[406] | 75 | for ( int j = 0; j < 4; ++j )
|
---|
[287] | 76 | {
|
---|
[406] | 77 | unsigned index = unsigned( vert.boneindex[j] );
|
---|
[323] | 78 | float weight = vert.boneweight[j];
|
---|
[287] | 79 | const quat& orient = m_transform[index].get_orientation();
|
---|
| 80 | const transform& offset = m_pos_offset[index];
|
---|
| 81 | m_pntdata[i].position += offset.transformed( vert.position ) * weight;
|
---|
| 82 | m_pntdata[i].normal += ( orient * vert.normal ) * weight;
|
---|
| 83 | m_pntdata[i].tangent += ( orient * vert.tangent ) * weight;
|
---|
| 84 | }
|
---|
| 85 | }
|
---|
| 86 | }
|
---|
| 87 |
|
---|
[302] | 88 | m_context->update( m_pbuffer, m_pntdata.data(), 0, m_pntdata.raw_size() );
|
---|
[227] | 89 | }
|
---|
| 90 | }
|
---|
| 91 |
|
---|
[289] | 92 |
|
---|
[419] | 93 | void nv::skeletal_animation_entry_cpu::initialize()
|
---|
| 94 | {
|
---|
[427] | 95 | for ( auto bone : *m_node_data )
|
---|
[419] | 96 | {
|
---|
[427] | 97 | if ( bone->size() > 0 )
|
---|
[419] | 98 | {
|
---|
[427] | 99 | m_interpolation_key = bone->get_interpolation_key();
|
---|
[419] | 100 | break;
|
---|
| 101 | }
|
---|
| 102 | }
|
---|
| 103 | }
|
---|
| 104 |
|
---|
[296] | 105 | void nv::skeletal_animation_entry_cpu::update_skeleton( transform* skeleton, float time ) const
|
---|
[289] | 106 | {
|
---|
[406] | 107 | float frame_duration = 1000.f / static_cast<float>( m_node_data->get_frame_rate() );
|
---|
[289] | 108 | float anim_duration = frame_duration * m_node_data->get_duration();
|
---|
| 109 | float new_time = fmodf( time, anim_duration ) * 0.001f;
|
---|
| 110 |
|
---|
| 111 | float frame_num = new_time * m_node_data->get_frame_rate();
|
---|
[428] | 112 | for ( size_t i = 0; i < m_node_data->size(); ++i )
|
---|
[289] | 113 | {
|
---|
[427] | 114 | raw_channel_interpolator interpolator( (*m_node_data)[i], m_interpolation_key );
|
---|
[419] | 115 | skeleton[i] = interpolator.get< transform >( frame_num );
|
---|
[289] | 116 | }
|
---|
| 117 | }
|
---|
| 118 |
|
---|
[288] | 119 | void nv::skeletal_animation_entry_gpu::initialize()
|
---|
[283] | 120 | {
|
---|
[287] | 121 | m_prepared = false;
|
---|
| 122 | m_children = nullptr;
|
---|
| 123 | m_offsets = nullptr;
|
---|
[428] | 124 | m_bone_ids = new sint16[m_node_data->size()];
|
---|
[287] | 125 |
|
---|
[419] | 126 | NV_ASSERT( m_node_data, "node data empty!" );
|
---|
| 127 |
|
---|
[287] | 128 | if ( !m_node_data->is_flat() )
|
---|
| 129 | {
|
---|
[428] | 130 | m_children = new vector< uint32 >[m_node_data->size()];
|
---|
| 131 | for ( uint32 n = 0; n < m_node_data->size(); ++n )
|
---|
[287] | 132 | {
|
---|
[427] | 133 | const data_channel_set* node = (*m_node_data)[n];
|
---|
| 134 | if ( node->get_parent_id() != -1 )
|
---|
[287] | 135 | {
|
---|
[427] | 136 | m_children[ node->get_parent_id()].push_back( n );
|
---|
[287] | 137 | }
|
---|
| 138 | }
|
---|
| 139 | }
|
---|
[283] | 140 | }
|
---|
| 141 |
|
---|
[287] | 142 | void nv::skeletal_animation_entry_gpu::update_skeleton( mat4* data, uint32 time ) const
|
---|
[283] | 143 | {
|
---|
[290] | 144 | float tick_time = ( time * 0.001f ) * m_frame_rate;
|
---|
| 145 | float anim_time = m_start;
|
---|
| 146 | if ( m_duration > 0.0f ) anim_time += fmodf( tick_time, m_duration );
|
---|
[287] | 147 |
|
---|
| 148 | if ( !m_node_data->is_flat() )
|
---|
| 149 | {
|
---|
| 150 | animate_rec( data, anim_time, 0, mat4() );
|
---|
| 151 | return;
|
---|
| 152 | }
|
---|
| 153 |
|
---|
[428] | 154 | for ( uint32 n = 0; n < m_node_data->size(); ++n )
|
---|
[287] | 155 | if ( m_bone_ids[n] >= 0 )
|
---|
| 156 | {
|
---|
[428] | 157 | const data_channel_set* node = (*m_node_data)[n];
|
---|
[427] | 158 | nv::mat4 node_mat( node->get_transform() );
|
---|
[287] | 159 |
|
---|
[427] | 160 | if ( node->size() > 0 )
|
---|
[287] | 161 | {
|
---|
[427] | 162 | raw_channel_interpolator interpolator( node, m_interpolation_key );
|
---|
[419] | 163 | node_mat = interpolator.get< mat4 >( anim_time );
|
---|
[287] | 164 | }
|
---|
| 165 |
|
---|
| 166 | sint16 bone_id = m_bone_ids[n];
|
---|
| 167 | data[ bone_id ] = node_mat * m_offsets[ bone_id ];
|
---|
| 168 | }
|
---|
[283] | 169 | }
|
---|
| 170 |
|
---|
[287] | 171 | void nv::skeletal_animation_entry_gpu::prepare( const mesh_nodes_data* bones )
|
---|
[283] | 172 | {
|
---|
[287] | 173 | if ( m_prepared ) return;
|
---|
[420] | 174 | unordered_map< uint64, uint16 > bone_names;
|
---|
[428] | 175 | m_offsets = new mat4[ bones->size() ];
|
---|
| 176 | for ( nv::uint16 bi = 0; bi < bones->size(); ++bi )
|
---|
[287] | 177 | {
|
---|
[428] | 178 | const data_channel_set* bone = (*bones)[ bi ];
|
---|
[431] | 179 | bone_names[ bone->get_name().value() ] = bi;
|
---|
[427] | 180 | m_offsets[bi] = bone->get_transform();
|
---|
[287] | 181 | }
|
---|
| 182 |
|
---|
[428] | 183 | for ( uint32 n = 0; n < m_node_data->size(); ++n )
|
---|
[287] | 184 | {
|
---|
[428] | 185 | const data_channel_set* node = (*m_node_data)[ n ];
|
---|
[287] | 186 | sint16 bone_id = -1;
|
---|
| 187 |
|
---|
[431] | 188 | auto bi = bone_names.find( node->get_name().value() );
|
---|
[287] | 189 | if ( bi != bone_names.end() )
|
---|
| 190 | {
|
---|
[406] | 191 | bone_id = sint16( bi->second );
|
---|
[287] | 192 | }
|
---|
| 193 | m_bone_ids[n] = bone_id;
|
---|
[419] | 194 |
|
---|
[427] | 195 | if ( m_interpolation_key.size() == 0 && node->size() > 0 )
|
---|
| 196 | m_interpolation_key = node->get_interpolation_key();
|
---|
[419] | 197 |
|
---|
[287] | 198 | }
|
---|
| 199 | m_prepared = true;
|
---|
[283] | 200 | }
|
---|
| 201 |
|
---|
[287] | 202 | void nv::skeletal_animation_entry_gpu::animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat ) const
|
---|
| 203 | {
|
---|
| 204 | // TODO: fix transforms, which are now embedded,
|
---|
| 205 | // see note in assimp_loader.cc:load_node
|
---|
[428] | 206 | const data_channel_set* node = ( *m_node_data )[ node_id ];
|
---|
[427] | 207 | mat4 node_mat( node->get_transform() );
|
---|
[287] | 208 |
|
---|
[427] | 209 | if ( node->size() > 0 )
|
---|
[287] | 210 | {
|
---|
[427] | 211 | raw_channel_interpolator interpolator( node, m_interpolation_key );
|
---|
[419] | 212 | node_mat = interpolator.get< mat4 >( time );
|
---|
[287] | 213 | }
|
---|
| 214 |
|
---|
| 215 | mat4 global_mat = parent_mat * node_mat;
|
---|
| 216 |
|
---|
| 217 | sint16 bone_id = m_bone_ids[ node_id ];
|
---|
| 218 | if ( bone_id >= 0 )
|
---|
| 219 | {
|
---|
| 220 | data[ bone_id ] = global_mat * m_offsets[ bone_id ];
|
---|
| 221 | }
|
---|
| 222 |
|
---|
| 223 | for ( auto child : m_children[ node_id ] )
|
---|
| 224 | {
|
---|
| 225 | animate_rec( data, time, child, global_mat );
|
---|
| 226 | }
|
---|
| 227 | }
|
---|
| 228 |
|
---|
| 229 | nv::skeletal_animation_entry_gpu::~skeletal_animation_entry_gpu()
|
---|
| 230 | {
|
---|
| 231 | delete[] m_offsets;
|
---|
| 232 | delete[] m_children;
|
---|
| 233 | delete[] m_bone_ids;
|
---|
| 234 | }
|
---|
| 235 |
|
---|
[430] | 236 | nv::skeletal_mesh_gpu::skeletal_mesh_gpu( context* a_context, const data_channel_set* a_mesh, const mesh_nodes_data* a_bone_data )
|
---|
[458] | 237 | : skeletal_mesh( a_context ), m_bone_data( a_bone_data ), m_index_count( 0 ), m_transform( nullptr )
|
---|
[287] | 238 | {
|
---|
[458] | 239 | if ( a_mesh )
|
---|
| 240 | {
|
---|
| 241 | m_va = a_context->create_vertex_array( a_mesh, nv::STATIC_DRAW );
|
---|
| 242 | m_index_count = a_mesh->get_channel_size( slot::INDEX );
|
---|
| 243 | }
|
---|
[287] | 244 | if ( m_bone_data )
|
---|
| 245 | {
|
---|
[428] | 246 | m_transform = new mat4[ m_bone_data->size() ];
|
---|
[287] | 247 | }
|
---|
| 248 | }
|
---|
| 249 |
|
---|
[283] | 250 | void nv::skeletal_mesh_gpu::update_animation( animation_entry* a_anim, uint32 a_anim_time )
|
---|
| 251 | {
|
---|
[287] | 252 | if ( m_bone_data && a_anim )
|
---|
[283] | 253 | {
|
---|
[406] | 254 | skeletal_animation_entry_gpu * anim = static_cast<skeletal_animation_entry_gpu*>( a_anim );
|
---|
[296] | 255 | anim->prepare( m_bone_data );
|
---|
[287] | 256 | anim->update_skeleton( m_transform, a_anim_time );
|
---|
[283] | 257 | }
|
---|
| 258 | }
|
---|
| 259 |
|
---|
[303] | 260 | void nv::skeletal_mesh_gpu::update( program a_program )
|
---|
[283] | 261 | {
|
---|
[287] | 262 | if ( m_bone_data )
|
---|
[428] | 263 | m_context->get_device()->set_opt_uniform_array( a_program, "nv_m_bones", m_transform, m_bone_data->size() );
|
---|
[287] | 264 | }
|
---|
| 265 |
|
---|
| 266 | nv::transform nv::skeletal_mesh_gpu::get_node_transform( uint32 node_id ) const
|
---|
| 267 | {
|
---|
[290] | 268 | if ( node_id == 0 ) return transform();
|
---|
[458] | 269 | if ( node_id == uint32(-1) ) return transform( m_transform[0] );
|
---|
[287] | 270 | return transform( m_transform[ node_id ] );
|
---|
| 271 | }
|
---|
| 272 |
|
---|
| 273 | nv::mat4 nv::skeletal_mesh_gpu::get_node_matrix( uint32 node_id ) const
|
---|
| 274 | {
|
---|
| 275 | return m_transform[ node_id ];
|
---|
| 276 | }
|
---|