commit eaf150fc52bf9b2e9ce15f59ca979a10f5c4cf86
parent e8ffa1ba81bfe5ba0398d37ab697fa53809c1bd9
Author: Michael Savage <mikejsavage@gmail.com>
Date: Wed, 29 May 2019 21:20:41 +0300
Working skinning
Diffstat:
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;