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.
|
---|
6 |
|
---|
7 | #include "nv/gfx/skeletal_mesh.hh"
|
---|
8 |
|
---|
9 | #include "nv/interface/context.hh"
|
---|
10 | #include "nv/interface/device.hh"
|
---|
11 | #include "nv/stl/unordered_map.hh"
|
---|
12 |
|
---|
13 | void nv::skeletal_animation_entry::initialize()
|
---|
14 | {
|
---|
15 | m_root = uint32(-1);
|
---|
16 | m_prepared = false;
|
---|
17 | m_children = nullptr;
|
---|
18 | m_offsets = nullptr;
|
---|
19 | m_bone_ids = new sint16[m_node_data->size()];
|
---|
20 |
|
---|
21 | NV_ASSERT( m_node_data, "node data empty!" );
|
---|
22 |
|
---|
23 | if ( !m_node_data->is_flat() )
|
---|
24 | {
|
---|
25 | m_children = new vector< uint32 >[m_node_data->size()];
|
---|
26 | for ( uint32 n = 0; n < m_node_data->size(); ++n )
|
---|
27 | {
|
---|
28 | const data_channel_set* node = (*m_node_data)[n];
|
---|
29 | if ( node->get_parent_id() != -1 )
|
---|
30 | {
|
---|
31 | m_children[ node->get_parent_id()].push_back( n );
|
---|
32 | }
|
---|
33 | else
|
---|
34 | {
|
---|
35 | if ( m_root >= 0 )
|
---|
36 | {
|
---|
37 | m_root = uint32( -2 );
|
---|
38 | }
|
---|
39 | else
|
---|
40 | m_root = n;
|
---|
41 | }
|
---|
42 | }
|
---|
43 | NV_ASSERT( m_root != uint32( -1 ), "Animation without root!" );
|
---|
44 | }
|
---|
45 | }
|
---|
46 |
|
---|
47 | void nv::skeletal_animation_entry::update_skeleton( mat4* data, uint32 a_ms_time ) const
|
---|
48 | {
|
---|
49 | float fframe = ( a_ms_time * 0.001f ) * m_fps;
|
---|
50 | uint32 frame = math::floor( fframe );
|
---|
51 | float reminder = fframe - static_cast<float>( frame );
|
---|
52 | uint32 duration = get_frame_count();
|
---|
53 | if ( duration == 0 )
|
---|
54 | {
|
---|
55 | frame = get_start_frame();
|
---|
56 | fframe = static_cast<float>( frame );
|
---|
57 | }
|
---|
58 | else if ( frame >= duration )
|
---|
59 | {
|
---|
60 | if ( is_looping() )
|
---|
61 | {
|
---|
62 | frame = frame % duration;
|
---|
63 | fframe = static_cast<float>( frame ) + reminder;
|
---|
64 | }
|
---|
65 | else
|
---|
66 | {
|
---|
67 | frame = get_end_frame();
|
---|
68 | fframe = static_cast<float>( frame );
|
---|
69 | }
|
---|
70 | }
|
---|
71 |
|
---|
72 | if ( !m_node_data->is_flat() )
|
---|
73 | {
|
---|
74 | if ( m_root == uint32( -2 ) ) // multi-root
|
---|
75 | {
|
---|
76 | for ( uint32 n = 0; n < m_node_data->size(); ++n )
|
---|
77 | if ( ( *m_node_data )[n]->get_parent_id() == -1 )
|
---|
78 | animate_rec( data, fframe, n, mat4() );
|
---|
79 | }
|
---|
80 | else
|
---|
81 | animate_rec( data, fframe, m_root, mat4() );
|
---|
82 | return;
|
---|
83 | }
|
---|
84 |
|
---|
85 | for ( uint32 n = 0; n < m_node_data->size(); ++n )
|
---|
86 | if ( m_bone_ids[n] >= 0 )
|
---|
87 | {
|
---|
88 | const data_channel_set* node = (*m_node_data)[n];
|
---|
89 | nv::mat4 node_mat( node->get_transform() );
|
---|
90 |
|
---|
91 | if ( node->size() > 0 )
|
---|
92 | {
|
---|
93 | raw_channel_interpolator interpolator( node, m_interpolation_key );
|
---|
94 | node_mat = interpolator.get< mat4 >( fframe );
|
---|
95 | }
|
---|
96 |
|
---|
97 | sint16 bone_id = m_bone_ids[n];
|
---|
98 | data[ bone_id ] = node_mat * m_offsets[ bone_id ];
|
---|
99 | }
|
---|
100 | }
|
---|
101 |
|
---|
102 | void nv::skeletal_animation_entry::prepare( const mesh_nodes_data* bones )
|
---|
103 | {
|
---|
104 | if ( m_prepared ) return;
|
---|
105 | nv::hash_store< shash64, uint16 > bone_names;
|
---|
106 | m_offsets = new mat4[ bones->size() ];
|
---|
107 | for ( nv::uint16 bi = 0; bi < bones->size(); ++bi )
|
---|
108 | {
|
---|
109 | const data_channel_set* bone = (*bones)[ bi ];
|
---|
110 | bone_names[ bone->get_name() ] = bi;
|
---|
111 | m_offsets[bi] = bone->get_transform();
|
---|
112 | }
|
---|
113 |
|
---|
114 | for ( uint32 n = 0; n < m_node_data->size(); ++n )
|
---|
115 | {
|
---|
116 | const data_channel_set* node = (*m_node_data)[ n ];
|
---|
117 | sint16 bone_id = -1;
|
---|
118 |
|
---|
119 | auto bi = bone_names.find( node->get_name() );
|
---|
120 | if ( bi != bone_names.end() )
|
---|
121 | {
|
---|
122 | bone_id = sint16( bi->second );
|
---|
123 | }
|
---|
124 | m_bone_ids[n] = bone_id;
|
---|
125 |
|
---|
126 | if ( m_interpolation_key.size() == 0 && node->size() > 0 )
|
---|
127 | m_interpolation_key = node->get_interpolation_key();
|
---|
128 |
|
---|
129 | }
|
---|
130 | m_prepared = true;
|
---|
131 | }
|
---|
132 |
|
---|
133 | void nv::skeletal_animation_entry::animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat ) const
|
---|
134 | {
|
---|
135 | // TODO: fix transforms, which are now embedded,
|
---|
136 | // see note in assimp_loader.cc:load_node
|
---|
137 | const data_channel_set* node = ( *m_node_data )[ node_id ];
|
---|
138 | mat4 node_mat( node->get_transform() );
|
---|
139 |
|
---|
140 | if ( node->size() > 0 )
|
---|
141 | {
|
---|
142 | raw_channel_interpolator interpolator( node, m_interpolation_key );
|
---|
143 | node_mat = interpolator.get< mat4 >( time );
|
---|
144 | }
|
---|
145 |
|
---|
146 | mat4 global_mat = parent_mat * node_mat;
|
---|
147 |
|
---|
148 | sint16 bone_id = m_bone_ids[ node_id ];
|
---|
149 | if ( bone_id >= 0 )
|
---|
150 | {
|
---|
151 | data[ bone_id ] = global_mat * m_offsets[ bone_id ];
|
---|
152 | }
|
---|
153 |
|
---|
154 | for ( auto child : m_children[ node_id ] )
|
---|
155 | {
|
---|
156 | animate_rec( data, time, child, global_mat );
|
---|
157 | }
|
---|
158 | }
|
---|
159 |
|
---|
160 | nv::skeletal_animation_entry::~skeletal_animation_entry()
|
---|
161 | {
|
---|
162 | delete[] m_offsets;
|
---|
163 | delete[] m_children;
|
---|
164 | delete[] m_bone_ids;
|
---|
165 | }
|
---|
166 |
|
---|
167 | nv::skeletal_mesh::skeletal_mesh( context* a_context, const data_channel_set* a_mesh, const mesh_nodes_data* a_bone_data )
|
---|
168 | : m_context( a_context ), m_bone_data( a_bone_data ), m_index_count( 0 ), m_transform( nullptr ), m_parent_id(-1)
|
---|
169 | {
|
---|
170 | if ( a_mesh )
|
---|
171 | {
|
---|
172 | m_va = a_context->create_vertex_array( a_mesh, nv::STATIC_DRAW );
|
---|
173 | m_index_count = a_mesh->get_channel_size( slot::INDEX );
|
---|
174 | m_parent_id = a_mesh->get_parent_id();
|
---|
175 | }
|
---|
176 | if ( m_bone_data )
|
---|
177 | {
|
---|
178 | m_transform = new mat4[ m_bone_data->size() ];
|
---|
179 | }
|
---|
180 | }
|
---|
181 |
|
---|
182 | void nv::skeletal_mesh::update_animation( animation_entry* a_anim, uint32 a_anim_time )
|
---|
183 | {
|
---|
184 | if ( m_bone_data && a_anim )
|
---|
185 | {
|
---|
186 | skeletal_animation_entry * anim = static_cast<skeletal_animation_entry*>( a_anim );
|
---|
187 | anim->prepare( m_bone_data );
|
---|
188 | anim->update_skeleton( m_transform, a_anim_time );
|
---|
189 | }
|
---|
190 | }
|
---|
191 |
|
---|
192 | void nv::skeletal_mesh::update( program a_program )
|
---|
193 | {
|
---|
194 | if ( m_bone_data )
|
---|
195 | m_context->get_device()->set_opt_uniform_array( a_program, "nv_m_bones", m_transform, m_bone_data->size() );
|
---|
196 | }
|
---|
197 |
|
---|
198 | nv::transform nv::skeletal_mesh::get_node_transform( uint32 node_id ) const
|
---|
199 | {
|
---|
200 | if ( node_id == 0 ) return transform();
|
---|
201 | if ( node_id == uint32(-1) ) return transform( m_transform[0] );
|
---|
202 | return transform( m_transform[ node_id ] );
|
---|
203 | }
|
---|
204 |
|
---|
205 | nv::mat4 nv::skeletal_mesh::get_node_matrix( uint32 node_id ) const
|
---|
206 | {
|
---|
207 | return m_transform[ node_id ];
|
---|
208 | }
|
---|