medfall

A super great game engine
Log | Files | Refs

commit eaf150fc52bf9b2e9ce15f59ca979a10f5c4cf86
parent e8ffa1ba81bfe5ba0398d37ab697fa53809c1bd9
Author: Michael Savage <mikejsavage@gmail.com>
Date:   Wed, 29 May 2019 21:20:41 +0300

Working skinning

Diffstat:
Mgltf.cc | 108++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mlinear_algebra.h | 12++++++++++++
Mrenderer.cc | 5++++-
Mshaders/skinned_flat_vertex_colours.glsl | 10+++++-----
4 files changed, 90 insertions(+), 45 deletions(-)

diff --git a/gltf.cc b/gltf.cc @@ -3,6 +3,7 @@ #include "log.h" #include "linear_algebra.h" #include "gl.h" +#include "immediate.h" #include "renderer.h" #include "shaders.h" @@ -34,6 +35,16 @@ SQT Lerp( const SQT & from, float t, const SQT & to ) { return res; } +inline void format( FormatBuffer * fb, const SQT & sqt, const FormatOpts & opts ) { + format( fb, "SQT(" ); + format( fb, sqt.rotation, opts ); + format( fb, ", " ); + format( fb, sqt.translation, opts ); + format( fb, ", " ); + format( fb, sqt.scale, opts ); + format( fb, ")" ); +} + static Mat4 SQTToMat4( const SQT & sqt ) { quat q = sqt.rotation; v3 t = sqt.translation; @@ -92,10 +103,6 @@ struct AnimationReel { u32 num_clips; }; -struct MatrixPalette { - Mat4 * joint_transforms; -}; - float PositiveMod( float x, float y ) { float res = fmodf( x, y ); if( res < 0 ) @@ -128,21 +135,15 @@ void SampleAnimationClip( const AnimationReel & reel, u32 clip_idx, float t, flo } } -void ComputeMatrixPalette( const AnimationReel & animation, const SQT * sqts, Mat4 * matrices ) { - todo2; - // skip the animation matrices to check i'm loading node hierarchy correctly - for( u32 i = 0; i < animation.num_joints; i++ ) { - matrices[ i ] = SQTToMat4( sqts[ i ] ); - } - - matrices[ 0 ] *= animation.joints[ 0 ].node_transform; +void ComputeMatrixPalette( const AnimationReel & animation, const SQT * sqts, Mat4 * joint_poses, Mat4 * skinning_matrices ) { + joint_poses[ 0 ] = SQTToMat4( sqts[ 0 ] ); for( u32 i = 1; i < animation.num_joints; i++ ) { u8 parent = animation.joints[ i ].parent; - matrices[ i ] *= matrices[ parent ] * animation.joints[ parent ].node_transform; + joint_poses[ i ] = joint_poses[ parent ] * SQTToMat4( sqts[ i ] ); } for( u32 i = 0; i < animation.num_joints; i++ ) { - matrices[ i ] *= animation.joints[ i ].joint_to_bind; + skinning_matrices[ i ] = joint_poses[ i ] * animation.joints[ i ].joint_to_bind; } } @@ -155,18 +156,18 @@ struct GLTFModel { // like cgltf_load_buffers, but doesn't try to load URIs static bool LoadBinaryBuffers( cgltf_data * data ) { - if( data->buffers_count && data->buffers[0].data == NULL && data->buffers[0].uri == NULL && data->bin ) { - if( data->bin_size < data->buffers[0].size ) - return false; - data->buffers[0].data = const_cast< void * >( data->bin ); - } - - for( cgltf_size i = 0; i < data->buffers_count; i++ ) { - if( data->buffers[i].data == NULL ) - return false; - } - - return true; + if( data->buffers_count && data->buffers[0].data == NULL && data->buffers[0].uri == NULL && data->bin ) { + if( data->bin_size < data->buffers[0].size ) + return false; + data->buffers[0].data = const_cast< void * >( data->bin ); + } + + for( cgltf_size i = 0; i < data->buffers_count; i++ ) { + if( data->buffers[i].data == NULL ) + return false; + } + + return true; } static AnimationReel::Clip CLIP; @@ -177,24 +178,21 @@ static void LoadSkin( const cgltf_skin * skin, std::map< const cgltf_node *, siz model.animation.joints = ( AnimationReel::Joint * ) malloc( sizeof( AnimationReel::Joint ) * skin->joints_count ); - todo1; + // todo1; // find root of skeleton, not necessarily root of hierarchy // bake transforms from root of skeleton to root of hierarchy into root joint // depth first fill in joints from node hierarchy to get rid of the ordering assert for( size_t i = 0; i < skin->joints_count; i++ ) { printf( "joint %zu: %p\n", i, skin->joints[ i ] ); - for( size_t j = 0; j < skin->joints[ i ]->children_count; j++ ) { - ASSERT( skin->joints[ i ]->children[ j ] > skin->joints[ i ] ); - } - node_to_joint[ skin->joints[ i ] ] = i; if( i == 0 ) { model.animation.joints[ i ].parent = 0; } else { - model.animation.joints[ i ].parent = checked_cast< u8 >( skin->joints[ i ]->parent - skin->joints[ 0 ] ); + model.animation.joints[ i ].parent = node_to_joint[ skin->joints[ i ]->parent ]; + ASSERT( model.animation.joints[ i ].parent < i ); } cgltf_node_transform_local( skin->joints[ i ], &model.animation.joints[ i ].node_transform.col0.x ); cgltf_bool ok = cgltf_accessor_read_float( skin->inverse_bind_matrices, i, &model.animation.joints[ i ].joint_to_bind.col0.x, 16 ); @@ -202,9 +200,6 @@ static void LoadSkin( const cgltf_skin * skin, std::map< const cgltf_node *, siz } model.animation.num_joints = skin->joints_count; - for( u8 i = 0; i < model.animation.num_joints; i++ ) { - ggprint( "{}: {} {}\n", i, model.animation.joints[ i ].parent, model.animation.joints[ i ].joint_to_bind ); - } } const char * pathtype( cgltf_animation_path_type type ) { @@ -349,10 +344,13 @@ static void LoadNode( MemoryArena * arena, const cgltf_node * node, std::map< co mesh_config.colours = renderer_new_vb( AccessorToSpan( attr.data ) ); } + // TODO: drop lanes that are never used? + // TODO: convert to u8 if( attr.type == cgltf_attribute_type_joints ) { mesh_config.joints = renderer_new_vb( AccessorToSpan( attr.data ) ); } + // TODO: convert to float3 and compute the last weight in the shader if( attr.type == cgltf_attribute_type_weights ) { mesh_config.weights = renderer_new_vb( AccessorToSpan( attr.data ) ); } @@ -442,16 +440,16 @@ GAME_FRAME( game_frame ) { SQT sample_sqt[ 100 ]; SampleAnimationClip( model.animation, 0, current_time, 0.0f, sample_sqt ); - Mat4 sample_matrices[ 100 ]; - ComputeMatrixPalette( model.animation, sample_sqt, sample_matrices ); + Mat4 joint_matrices[ 100 ]; + Mat4 skinning_matrices[ 100 ]; + ComputeMatrixPalette( model.animation, sample_sqt, joint_matrices, skinning_matrices ); renderer_begin_frame(); renderer_begin_pass( RENDERER_CLEAR_COLOUR_DO, RENDERER_CLEAR_DEPTH_DO ); UniformBinding view_uniforms = renderer_uniforms( V, P, game->pos ); - UniformBinding joints_uniforms = renderer_upload_uniforms( sample_matrices, model.animation.num_joints * sizeof( Mat4 ), 16 ); + UniformBinding joints_uniforms = renderer_upload_uniforms( skinning_matrices, model.animation.num_joints * sizeof( Mat4 ), 16 ); - todo3; // render bones without the mesh attached to make sure transforms are correct { RenderState render_state; @@ -465,6 +463,38 @@ GAME_FRAME( game_frame ) { } } + // draw an arrow on the guy's head + { + u8 head_joint = 4; + v3 pos = ( joint_matrices[ head_joint ] * v4( 0, 0, 0, 1 ) ).xyz(); + v3 up = ( joint_matrices[ head_joint ] * v4( 0, 1, 0, 0 ) ).xyz(); + + immediate_arrow( pos, up, 1, v4( 0, 1, 0, 1 ) ); + + RenderState render_state; + render_state.shader = get_shader( SHADER_FLAT_VERTEX_COLOURS ); + render_state.uniforms[ UNIFORMS_VIEW ] = renderer_uniforms( V, P ); + immediate_render( render_state ); + } + + // { + // for( u32 i = 0; i < model.animation.num_joints; i++ ) { + // v3 pos = ( sample_matrices[ i ] * v4( 0, 0, 0, 1 ) ).xyz(); + // v3 parent_pos = ( sample_matrices[ model.animation.joints[ i ].parent ] * v4( 0, 0, 0, 1 ) ).xyz(); + // v3 forward = ( sample_matrices[ i ] * v4( 0, 1, 0, 0 ) ).xyz(); + // + // immediate_sphere( pos, 0.025, v4( 1 ) ); + // immediate_arrow( pos, forward, 0.2, v4( 0.5, 0.5, 0.5, 1 ) ); + // if( i != 0 ) + // immediate_arrow( pos, ( parent_pos - pos ), 0.1, v4( 1, 0, 0, 1 ) ); + // } + // + // RenderState render_state; + // render_state.shader = get_shader( SHADER_FLAT_VERTEX_COLOURS ); + // render_state.uniforms[ UNIFORMS_VIEW ] = renderer_uniforms( V, P ); + // immediate_render( render_state ); + // } + renderer_end_pass(); renderer_end_frame(); } diff --git a/linear_algebra.h b/linear_algebra.h @@ -1091,6 +1091,18 @@ inline void format( FormatBuffer * fb, const m4 & m, const FormatOpts & opts ) { format( fb, ")" ); } +inline void format( FormatBuffer * fb, const quat & q, const FormatOpts & opts ) { + format( fb, "quat(" ); + format( fb, q.x, opts ); + format( fb, ", " ); + format( fb, q.y, opts ); + format( fb, ", " ); + format( fb, q.z, opts ); + format( fb, ", " ); + format( fb, q.w, opts ); + format( fb, ")" ); +} + /* * autogenerated visitor header */ diff --git a/renderer.cc b/renderer.cc @@ -1044,7 +1044,10 @@ static void setup_vertex_attrib( GLuint index, VertexFormat format, u32 stride = vertexfmt_to_glenum( format, &type, &components ); glEnableVertexAttribArray( index ); - glVertexAttribPointer( index, components, type, GL_FALSE, stride, gl_offset ); + if( index == ATTR_JOINTS ) + glVertexAttribIPointer( index, components, type, stride, gl_offset ); + else + glVertexAttribPointer( index, components, type, GL_FALSE, stride, gl_offset ); } Mesh renderer_new_mesh( MeshConfig config ) { diff --git a/shaders/skinned_flat_vertex_colours.glsl b/shaders/skinned_flat_vertex_colours.glsl @@ -3,7 +3,7 @@ in vec4 position; in vec3 colour; -in vec4 joints; +in uvec4 joints; in vec4 weights; out vec3 frag_colour; @@ -19,10 +19,10 @@ layout( std140 ) uniform animation { void main() { mat4 skin = - weights.x * joint_poses[ int( joints.x ) ] + - weights.y * joint_poses[ int( joints.y ) ] + - weights.z * joint_poses[ int( joints.z ) ] + - weights.w * joint_poses[ int( joints.w ) ]; + weights.x * joint_poses[ joints.x ] + + weights.y * joint_poses[ joints.y ] + + weights.z * joint_poses[ joints.z ] + + weights.w * joint_poses[ joints.w ]; gl_Position = P * V * skin * position; frag_colour = colour;