source: trunk/src/gfx/skeletal_mesh.cc @ 299

Last change on this file since 299 was 299, checked in by epyon, 11 years ago
  • all bind and update function for graphics objects are done via context (will simplify directx device, and allow for handles instead of objects)
File size: 6.9 KB
Line 
1// Copyright (C) 2011 Kornel Kisielewicz
2// This file is part of NV Libraries.
3// For conditions of distribution and use, see copyright notice in nv.hh
4
5#include "nv/gfx/skeletal_mesh.hh"
6
7#include <glm/gtc/matrix_access.hpp>
8#include <glm/gtx/matrix_interpolation.hpp>
9#include "nv/interface/context.hh"
10#include "nv/interface/device.hh"
11
12
13nv::skeletal_mesh_cpu::skeletal_mesh_cpu( context* a_context, const mesh_data* a_mesh_data, const mesh_nodes_data* bones )
14        : skeletal_mesh()
15        , m_context( a_context )
16        , m_data( a_mesh_data )
17{
18        const mesh_raw_channel* pnt_chan = a_mesh_data->get_channel<md5_vtx_pnt>();
19        m_pntdata.assign( (const md5_vtx_pnt*)pnt_chan->data, pnt_chan->count );
20        m_bone_offset.resize( bones->get_count() );
21        m_transform.resize( bones->get_count() );
22
23        for ( uint32 i = 0; i < bones->get_count(); ++i )
24        {
25                m_bone_offset[i] = transform( bones->get_node(i)->transform );
26        }
27        m_vtx_data  = a_mesh_data->get_channel_data<md5_vtx_pntiw>();
28        m_indices   = a_mesh_data->get_count();
29        m_va        = a_context->get_device()->create_vertex_array( a_mesh_data, nv::STREAM_DRAW );
30}
31
32void nv::skeletal_mesh_cpu::update_animation( animation_entry* a_anim, uint32 a_anim_time )
33{
34        if ( a_anim )
35        {
36                skeletal_animation_entry_cpu * anim = (skeletal_animation_entry_cpu*)a_anim;
37                anim->update_skeleton( m_transform.data(), (float)a_anim_time );
38                {
39                        size_t skeleton_size = m_bone_offset.size();
40                        size_t vertex_count  = m_pntdata.size();
41                        m_pos_offset.resize( skeleton_size );
42                        for ( unsigned int i = 0; i < skeleton_size; ++i )
43                        {
44                                m_pos_offset[i] = m_transform[i] * m_bone_offset[i];
45                        }
46
47                        std::fill( m_pntdata.raw_data(), m_pntdata.raw_data() + m_pntdata.raw_size(), 0 );
48                        for ( unsigned int i = 0; i < vertex_count; ++i )
49                        {
50                                const md5_vtx_pntiw& vert = m_vtx_data[i];
51
52                                for ( size_t j = 0; j < 4; ++j )
53                                {
54                                        int   index  = vert.boneindex[j];
55                                        float weight = vert.boneweight[j];
56                                        const quat& orient      = m_transform[index].get_orientation();
57                                        const transform& offset = m_pos_offset[index];
58                                        m_pntdata[i].position += offset.transformed( vert.position )        * weight;
59                                        m_pntdata[i].normal   += ( orient * vert.normal  ) * weight;
60                                        m_pntdata[i].tangent  += ( orient * vert.tangent ) * weight;
61                                }
62                        }
63                }
64
65                vertex_buffer* vb = m_va->find_buffer( nv::slot::POSITION );
66                m_context->update( vb, m_pntdata.data(), 0, m_pntdata.raw_size() );
67        }
68}
69
70
71void nv::skeletal_animation_entry_cpu::update_skeleton( transform* skeleton, float time ) const
72{
73        float frame_duration = 1000.f / (float)m_node_data->get_frame_rate();
74        float anim_duration = frame_duration * m_node_data->get_duration();
75        float new_time = fmodf( time, anim_duration ) * 0.001f;
76
77        float frame_num = new_time * m_node_data->get_frame_rate();
78        for ( size_t i = 0; i < m_node_data->get_count(); ++i )
79        {
80                skeleton[i] = m_node_data->get_node(i)->data->get_transform( frame_num );
81        }
82}
83
84
85
86nv::skeletal_mesh_cpu::~skeletal_mesh_cpu()
87{
88        delete m_va;
89}
90
91void nv::skeletal_animation_entry_gpu::initialize()
92{
93        m_prepared  = false;
94        m_children  = nullptr;
95        m_offsets   = nullptr;
96        uint32 node_count = m_node_data->get_count();
97        m_bone_ids  = new sint16[ node_count ];
98
99        if ( !m_node_data->is_flat() )
100        {
101                m_children = new std::vector< uint32 >[ node_count ];
102                for ( uint32 n = 0; n < node_count; ++n )
103                {
104                        const mesh_node_data* node = m_node_data->get_node(n);
105                        if ( node->parent_id != -1 )
106                        {
107                                m_children[ node->parent_id ].push_back( n );
108                        }
109                }
110        }
111}
112
113void nv::skeletal_animation_entry_gpu::update_skeleton( mat4* data, uint32 time ) const
114{
115        float tick_time = ( time * 0.001f ) * m_frame_rate;
116        float anim_time = m_start;
117        if ( m_duration > 0.0f ) anim_time += fmodf( tick_time, m_duration );
118
119        if ( !m_node_data->is_flat() )
120        {
121                animate_rec( data, anim_time, 0, mat4() );
122                return;
123        }
124
125        for ( uint32 n = 0; n < m_node_data->get_count(); ++n )
126                if ( m_bone_ids[n] >= 0 )
127                {
128                        const mesh_node_data* node = m_node_data->get_node(n);
129                        nv::mat4 node_mat( node->transform );
130
131                        if ( node->data )
132                        {
133                                node_mat = node->data->get_matrix( anim_time );
134                        }
135
136                        sint16 bone_id = m_bone_ids[n];
137                        data[ bone_id ] = node_mat * m_offsets[ bone_id ];
138                }
139}
140
141void nv::skeletal_animation_entry_gpu::prepare( const mesh_nodes_data* bones )
142{
143        if ( m_prepared ) return;
144        std::unordered_map< std::string, nv::uint16 > bone_names;
145        m_offsets = new mat4[ bones->get_count() ];
146        for ( nv::uint16 bi = 0; bi < bones->get_count(); ++bi )
147        {
148                const mesh_node_data* bone = bones->get_node(bi);
149                bone_names[ bone->name ] = bi;
150                m_offsets[bi] = bone->transform;
151        }
152
153        for ( uint32 n = 0; n < m_node_data->get_count(); ++n )
154        {
155                const mesh_node_data* node = m_node_data->get_node(n);
156                sint16 bone_id = -1;
157
158                auto bi = bone_names.find( node->name );
159                if ( bi != bone_names.end() )
160                {
161                        bone_id = bi->second;
162                }
163                m_bone_ids[n] = bone_id;
164        }
165        m_prepared = true;
166}
167
168void nv::skeletal_animation_entry_gpu::animate_rec( mat4* data, float time, uint32 node_id, const mat4& parent_mat ) const
169{
170        // TODO: fix transforms, which are now embedded,
171        //       see note in assimp_loader.cc:load_node
172        const mesh_node_data* node = m_node_data->get_node( node_id );
173        mat4 node_mat( node->transform );
174
175        if ( node->data )
176        {
177                node_mat = node->data->get_matrix( time );
178        }
179
180        mat4 global_mat = parent_mat * node_mat;
181
182        sint16 bone_id = m_bone_ids[ node_id ];
183        if ( bone_id >= 0 )
184        {
185                data[ bone_id ] = global_mat * m_offsets[ bone_id ];
186        }
187
188        for ( auto child : m_children[ node_id ] )
189        {
190                animate_rec( data, time, child, global_mat );
191        }
192}
193
194nv::skeletal_animation_entry_gpu::~skeletal_animation_entry_gpu()
195{
196        delete[] m_offsets;
197        delete[] m_children;
198        delete[] m_bone_ids;
199}
200
201nv::skeletal_mesh_gpu::skeletal_mesh_gpu( context* a_context, const mesh_data* a_mesh, const mesh_nodes_data* a_bone_data )
202        : skeletal_mesh(), m_bone_data( a_bone_data ), m_transform( nullptr )
203{
204        m_va          = a_context->get_device()->create_vertex_array( a_mesh, nv::STATIC_DRAW );
205        m_index_count = a_mesh->get_count();
206        if ( m_bone_data )
207        {
208                m_transform = new mat4[ m_bone_data->get_count() ];
209        }
210}
211
212void nv::skeletal_mesh_gpu::update_animation( animation_entry* a_anim, uint32 a_anim_time )
213{
214        if ( m_bone_data && a_anim )
215        {
216                skeletal_animation_entry_gpu * anim = (skeletal_animation_entry_gpu*)a_anim;
217                anim->prepare( m_bone_data );
218                anim->update_skeleton( m_transform, a_anim_time );
219        }
220}
221
222void nv::skeletal_mesh_gpu::update( program* a_program )
223{
224        if ( m_bone_data )
225                a_program->set_opt_uniform_array( "nv_m_bones", m_transform, m_bone_data->get_count() );
226}
227
228nv::transform nv::skeletal_mesh_gpu::get_node_transform( uint32 node_id ) const
229{
230        if ( node_id == 0 ) return transform();
231        return transform( m_transform[ node_id ] );
232}
233
234nv::mat4 nv::skeletal_mesh_gpu::get_node_matrix( uint32 node_id ) const
235{
236        return m_transform[ node_id ];
237}
Note: See TracBrowser for help on using the repository browser.