medfall

A super great game engine
Log | Files | Refs

gltf.cc (19434B)


      1 #include "game.h"
      2 #include "intrinsics.h"
      3 #include "log.h"
      4 #include "linear_algebra.h"
      5 #include "gl.h"
      6 #include "immediate.h"
      7 #include "renderer.h"
      8 #include "shaders.h"
      9 
     10 #include "platform_time.h"
     11 
     12 #include "libs/cgltf/cgltf.h"
     13 
     14 typedef quat Quaternion;
     15 typedef v3 Vec3;
     16 typedef m4 Mat4;
     17 
     18 #define Lerp lerp
     19 #define NLerp nlerp
     20 
     21 struct SQT {
     22 	Quaternion rotation;
     23 	Vec3 translation;
     24 	float scale;
     25 };
     26 
     27 inline void format( FormatBuffer * fb, const SQT & sqt, const FormatOpts & opts ) {
     28 	format( fb, "SQT(" );
     29 	format( fb, sqt.rotation, opts );
     30 	format( fb, ", " );
     31 	format( fb, sqt.translation, opts );
     32 	format( fb, ", " );
     33 	format( fb, sqt.scale, opts );
     34 	format( fb, ")" );
     35 }
     36 
     37 static Mat4 SQTToMat4( const SQT & sqt ) {
     38 	quat q = sqt.rotation;
     39 	v3 t = sqt.translation;
     40 	float s = sqt.scale;
     41 
     42 	// return t * q * s;
     43 	return Mat4(
     44 		( 1.0f - 2 * q.y * q.y - 2.0f * q.z * q.z ) * s,
     45 		( 2.0f * q.x * q.y - 2.0f * q.z * q.w ) * s,
     46 		( 2.0f * q.x * q.z + 2.0f * q.y * q.w ) * s,
     47 		t.x,
     48 
     49 		( 2.0f * q.x * q.y + 2.0f * q.z * q.w ) * s,
     50 		( 1.0f - 2.0f * q.x * q.x - 2.0f * q.z * q.z ) * s,
     51 		( 2.0f * q.y * q.z - 2.0f * q.x * q.w ) * s,
     52 		t.y,
     53 
     54 		( 2.0f * q.x * q.z - 2.0f * q.y * q.w ) * s,
     55 		( 2.0f * q.y * q.z + 2.0f * q.x * q.w ) * s,
     56 		( 1.0f - 2.0f * q.x * q.x - 2.0f * q.y * q.y ) * s,
     57 		t.z,
     58 
     59 		0.0f, 0.0f, 0.0f, 1.0f
     60 	);
     61 }
     62 
     63 struct AnimationReel {
     64 	template< typename T >
     65 	struct Channel {
     66 		T * samples;
     67 		float * times;
     68 		u32 num_samples;
     69 	};
     70 
     71 	struct Joint {
     72 		Mat4 joint_to_bind;
     73 		u8 parent;
     74 		u8 next;
     75 
     76 		Channel< Quaternion > rotations;
     77 		Channel< Vec3 > translations;
     78 		Channel< float > scales;
     79 	};
     80 
     81 	struct Clip {
     82 		float start_time;
     83 		float duration;
     84 		bool loop;
     85 	};
     86 
     87 	Joint * joints;
     88 	u8 num_joints;
     89 	u8 root_joint;
     90 
     91 	Clip * clips;
     92 	u32 num_clips;
     93 };
     94 
     95 float PositiveMod( float x, float y ) {
     96 	float res = fmodf( x, y );
     97 	if( res < 0 )
     98 		res += y;
     99 	return res;
    100 }
    101 
    102 void SampleAnimationChannel( const float * times, u32 n, float t, u32 * sample, float * lerp_frac ) {
    103 	t = clamp( times[ 0 ], t, times[ n - 1 ] );
    104 
    105 	*sample = 0;
    106 	for( u32 i = 1; i < n; i++ ) {
    107 		if( times[ i ] >= t ) {
    108 			*sample = i - 1;
    109 			break;
    110 		}
    111 	}
    112 
    113 	*lerp_frac = ( t - times[ *sample ] ) / ( times[ *sample + 1 ] - times[ *sample ] );
    114 }
    115 
    116 void SampleAnimationClip( const AnimationReel & reel, u32 clip_idx, float t, float start_time, SQT * joint_poses ) {
    117 	AnimationReel::Clip clip = reel.clips[ clip_idx ];
    118 
    119 	float local_time;
    120 	if( clip.loop ) {
    121 		local_time = PositiveMod( t - start_time, clip.duration );
    122 	}
    123 	else {
    124 		local_time = clamp( 0.0f, t - start_time, clip.duration );
    125 	}
    126 
    127 	for( u8 i = 0; i < reel.num_joints; i++ ) {
    128 		joint_poses[ i ].rotation = quat_identity();
    129                 joint_poses[ i ].translation = Vec3( 0 );
    130                 joint_poses[ i ].scale = 1.0f;
    131 
    132                 const AnimationReel::Joint & joint = reel.joints[ i ];
    133 
    134                 if( joint.rotations.samples != NULL ) {
    135 			if( joint.rotations.num_samples == 1 ) {
    136 				joint_poses[ i ].rotation = joint.rotations.samples[ 0 ];
    137 			}
    138 			else {
    139 				u32 sample;
    140 				float lerp_frac;
    141 				SampleAnimationChannel( joint.rotations.times, joint.rotations.num_samples, local_time, &sample, &lerp_frac );
    142 				joint_poses[ i ].rotation = NLerp( joint.rotations.samples[ sample ], lerp_frac, joint.rotations.samples[ ( sample + 1 ) % joint.rotations.num_samples ] );
    143 			}
    144                 }
    145 
    146                 if( joint.translations.samples != NULL ) {
    147 			if( joint.translations.num_samples == 1 ) {
    148 				joint_poses[ i ].translation = joint.translations.samples[ 0 ];
    149 			}
    150 			else {
    151 				u32 sample;
    152 				float lerp_frac;
    153 				SampleAnimationChannel( joint.translations.times, joint.translations.num_samples, local_time, &sample, &lerp_frac );
    154 				joint_poses[ i ].translation = Lerp( joint.translations.samples[ sample ], lerp_frac, joint.translations.samples[ ( sample + 1 ) % joint.translations. num_samples ] );
    155 			}
    156                 }
    157 
    158                 if( joint.scales.samples != NULL ) {
    159 			if( joint.scales.num_samples == 1 ) {
    160 				joint_poses[ i ].scale = joint.scales.samples[ 0 ];
    161 			}
    162 			else {
    163 				u32 sample;
    164 				float lerp_frac;
    165 				SampleAnimationChannel( joint.scales.times, joint.scales.num_samples, local_time, &sample, &lerp_frac );
    166 				joint_poses[ i ].scale = Lerp( joint.scales.samples[ sample ], lerp_frac, joint.scales.samples[ ( sample + 1 ) % joint.scales.num_samples ] );
    167 			}
    168                 }
    169         }
    170 }
    171 
    172 void ComputeMatrixPalette( const AnimationReel & animation, const SQT * sqts, Mat4 * joint_poses, Mat4 * skinning_matrices ) {
    173 	u8 joint_idx = animation.root_joint;
    174 	for( u32 i = 0; i < animation.num_joints; i++ ) {
    175 		u8 parent = animation.joints[ joint_idx ].parent;
    176 		if( parent != U8_MAX ) {
    177 			joint_poses[ joint_idx ] = joint_poses[ parent ] * SQTToMat4( sqts[ joint_idx ] );
    178 		}
    179 		else {
    180 			joint_poses[ joint_idx ] = SQTToMat4( sqts[ joint_idx ] );
    181 		}
    182 		joint_idx = animation.joints[ joint_idx ].next;
    183 	}
    184 
    185 	for( u32 i = 0; i < animation.num_joints; i++ ) {
    186 		skinning_matrices[ i ] = joint_poses[ i ] * animation.joints[ i ].joint_to_bind;
    187 	}
    188 }
    189 
    190 struct GLTFModel {
    191 	Mesh meshes[ 16 ];
    192 	u32 num_meshes;
    193 
    194 	AnimationReel animation;
    195 };
    196 
    197 // like cgltf_load_buffers, but doesn't try to load URIs
    198 static bool LoadBinaryBuffers( cgltf_data * data ) {
    199 	if( data->buffers_count && data->buffers[0].data == NULL && data->buffers[0].uri == NULL && data->bin ) {
    200 		if( data->bin_size < data->buffers[0].size )
    201 			return false;
    202 		data->buffers[0].data = const_cast< void * >( data->bin );
    203 	}
    204 
    205 	for( cgltf_size i = 0; i < data->buffers_count; i++ ) {
    206 		if( data->buffers[i].data == NULL )
    207 			return false;
    208 	}
    209 
    210 	return true;
    211 }
    212 
    213 static AnimationReel::Clip CLIP;
    214 static GLTFModel model;
    215 
    216 static u8 GetJointIdx( const cgltf_node * node ) {
    217 	return u8( uintptr_t( node->camera ) - 1 );
    218 }
    219 
    220 static void SetJointIdx( cgltf_node * node, u8 joint_idx ) {
    221 	node->camera = ( cgltf_camera * ) uintptr_t( joint_idx + 1 );
    222 }
    223 
    224 static void AddJoint( const cgltf_skin * skin, cgltf_node * node, u8 ** prev ) {
    225 	u8 joint_idx = GetJointIdx( node );
    226 	**prev = joint_idx;
    227 	*prev = &model.animation.joints[ joint_idx ].next;
    228 
    229 	model.animation.joints[ joint_idx ].parent = node->parent != NULL ? GetJointIdx( node->parent ) : U8_MAX;
    230 
    231 	cgltf_bool ok = cgltf_accessor_read_float( skin->inverse_bind_matrices, joint_idx, model.animation.joints[ joint_idx ].joint_to_bind.ptr(), 16 );
    232 	ASSERT( ok != 0 );
    233 
    234 	for( size_t i = 0; i < node->children_count; i++ ) {
    235 		AddJoint( skin, node->children[ i ], prev );
    236 	}
    237 }
    238 
    239 static void LoadSkin( const cgltf_skin * skin ) {
    240 	model.animation.joints = ( AnimationReel::Joint * ) malloc( sizeof( AnimationReel::Joint ) * skin->joints_count );
    241 	model.animation.num_joints = skin->joints_count;
    242 	memset( model.animation.joints, 0, sizeof( AnimationReel::Joint ) * skin->joints_count );
    243 
    244 	for( size_t i = 0; i < skin->joints_count; i++ ) {
    245 		SetJointIdx( skin->joints[ i ], i );
    246 	}
    247 
    248 	u8 * prev_ptr = &model.animation.root_joint;
    249 	for( size_t i = 0; i < skin->joints_count; i++ ) {
    250 		if( skin->joints[ i ]->parent == NULL || GetJointIdx( skin->joints[ i ]->parent ) == U8_MAX ) {
    251 			ggprint( "root {}\n", skin->joints[ i ]->name );
    252 			AddJoint( skin, skin->joints[ i ], &prev_ptr );
    253 		}
    254 	}
    255 }
    256 
    257 const char * pathtype( cgltf_animation_path_type type ) {
    258 	if( type == cgltf_animation_path_type_translation ) return "translation";
    259 	if( type == cgltf_animation_path_type_rotation ) return "rotation";
    260 	if( type == cgltf_animation_path_type_scale ) return "scale";
    261 	if( type == cgltf_animation_path_type_weights ) return "weights";
    262 	return "invalid";
    263 }
    264 
    265 template< typename T >
    266 static void LoadChannel( const cgltf_animation_channel * chan, AnimationReel::Channel< T > * out_channel ) {
    267 	constexpr size_t lanes = sizeof( T ) / sizeof( float );
    268 	size_t n = chan->sampler->input->count;
    269 
    270 	float * memory = ( float * ) malloc( n * ( lanes + 1 ) * sizeof( float ) );
    271 	out_channel->times = memory;
    272 	out_channel->samples = ( T * ) ( memory + n );
    273 	out_channel->num_samples = n;
    274 
    275 	for( size_t i = 0; i < n; i++ ) {
    276 		cgltf_bool ok = cgltf_accessor_read_float( chan->sampler->input, i, &out_channel->times[ i ], 1 );
    277 		ok = ok && cgltf_accessor_read_float( chan->sampler->output, i, out_channel->samples[ i ].ptr(), lanes );
    278 		ASSERT( ok != 0 );
    279 	}
    280 }
    281 
    282 static void LoadScaleChannel( const cgltf_animation_channel * chan, AnimationReel::Channel< float > * out_channel ) {
    283 	size_t n = chan->sampler->input->count;
    284 
    285 	float * memory = ( float * ) malloc( n * 2 * sizeof( float ) );
    286 	out_channel->times = memory;
    287 	out_channel->samples = memory + n;
    288 	out_channel->num_samples = n;
    289 
    290 	for( size_t i = 0; i < n; i++ ) {
    291 		cgltf_bool ok = cgltf_accessor_read_float( chan->sampler->input, i, &out_channel->times[ i ], 1 );
    292 		ASSERT( ok != 0 );
    293 
    294 		float scale[ 3 ];
    295 		ok = ok && cgltf_accessor_read_float( chan->sampler->output, i, scale, 3 );
    296 		ASSERT( ok != 0 );
    297 
    298 		ASSERT( fabsf( scale[ 0 ] / scale[ 1 ] - 1.0f ) < 0.001f );
    299 		ASSERT( fabsf( scale[ 0 ] / scale[ 2 ] - 1.0f ) < 0.001f );
    300 
    301 		out_channel->samples[ i ] = scale[ 0 ];
    302 	}
    303 }
    304 
    305 static void LoadAnimationReel( MemoryArena * arena, const cgltf_animation * animation ) {
    306 	ggprint( "LoadAnimationReel\n" );
    307 
    308 	for( size_t i = 0; i < animation->channels_count; i++ ) {
    309 		const cgltf_animation_channel * chan = &animation->channels[ i ];
    310 
    311 		u8 joint_idx = GetJointIdx( chan->target_node );
    312 		ASSERT( joint_idx != U8_MAX );
    313 
    314 		if( chan->target_path == cgltf_animation_path_type_translation ) {
    315 			ggprint( "{} translation\n", chan->target_node->name );
    316 			LoadChannel( chan, &model.animation.joints[ joint_idx ].translations );
    317 		}
    318 		else if( chan->target_path == cgltf_animation_path_type_rotation ) {
    319 			ggprint( "{} rotation\n", chan->target_node->name );
    320 			LoadChannel( chan, &model.animation.joints[ joint_idx ].rotations );
    321 		}
    322 		else if( chan->target_path == cgltf_animation_path_type_scale ) {
    323 			ggprint( "{} scale\n", chan->target_node->name );
    324 			LoadScaleChannel( chan, &model.animation.joints[ joint_idx ].scales );
    325 		}
    326 		else {
    327 			FATAL( "bad path type" );
    328 		}
    329 	}
    330 
    331 	CLIP.start_time = 0;
    332 	CLIP.duration = 10;
    333 	CLIP.loop = true;
    334 
    335 	model.animation.clips = &CLIP;
    336 	model.animation.num_clips = 1;
    337 }
    338 
    339 template< typename T, size_t N >
    340 static void CreateSingleSampleChannel( AnimationReel::Channel< T > * out_channel, const float ( &sample )[ N ] ) {
    341 	constexpr size_t lanes = sizeof( T ) / sizeof( float );
    342 	STATIC_ASSERT( lanes == N );
    343 
    344 	float * memory = ( float * ) malloc( ( N + 1 ) * sizeof( float ) );
    345 	out_channel->times = memory;
    346 	out_channel->samples = ( T * ) ( memory + 1 );
    347 	out_channel->num_samples = 1;
    348 
    349 	out_channel->times[ 0 ] = 0.0f;
    350 	memcpy( &out_channel->samples[ 0 ], sample, N * sizeof( float ) );
    351 }
    352 
    353 static void FixupMissingAnimationChannels( const cgltf_skin * skin ) {
    354 	for( u8 i = 0; i < model.animation.num_joints; i++ ) {
    355 		const cgltf_node * node = skin->joints[ i ];
    356 		AnimationReel::Joint & joint = model.animation.joints[ i ];
    357 
    358 		if( joint.rotations.samples == NULL && node->has_rotation ) {
    359 			CreateSingleSampleChannel( &joint.rotations, node->rotation );
    360 		}
    361 
    362 		if( joint.translations.samples == NULL && node->has_translation ) {
    363 			CreateSingleSampleChannel( &joint.translations, node->translation );
    364 		}
    365 
    366 		if( joint.scales.samples == NULL && node->has_scale ) {
    367 			ASSERT( fabsf( node->scale[ 0 ] / node->scale[ 1 ] - 1.0f ) < 0.001f );
    368 			ASSERT( fabsf( node->scale[ 0 ] / node->scale[ 2 ] - 1.0f ) < 0.001f );
    369 			float scale[ 1 ] = { node->scale[ 0 ] };
    370 			CreateSingleSampleChannel( &joint.scales, scale );
    371 		}
    372 	}
    373 }
    374 
    375 array< const u8 > AccessorToSpan( const cgltf_accessor * accessor ) {
    376 	cgltf_size offset = accessor->offset + accessor->buffer_view->offset;
    377 	return array< const u8 >( ( const u8 * ) accessor->buffer_view->buffer->data + offset, accessor->count * accessor->stride );
    378 }
    379 
    380 static void LoadNode( MemoryArena * arena, const cgltf_node * node, bool animated, int depth ) {
    381 	MEMARENA_SCOPED_CHECKPOINT( arena );
    382 
    383 	Mat4 transform;
    384 	{
    385 		cgltf_node_transform_local( node, transform.ptr() );
    386 	}
    387 
    388 	for( size_t i = 0; i < node->children_count; i++ ) {
    389 		LoadNode( arena, node->children[ i ], animated, depth + 1 );
    390 	}
    391 
    392 	if( node->mesh == NULL )
    393 		return;
    394 
    395 	ASSERT( node->mesh->primitives_count == 1 );
    396 
    397 	const cgltf_primitive & prim = node->mesh->primitives[ 0 ];
    398 
    399 	MeshConfig mesh_config;
    400 
    401 	// TODO: handle packed attributes
    402 	for( size_t i = 0; i < prim.attributes_count; i++ ) {
    403 		const cgltf_attribute & attr = prim.attributes[ i ];
    404 
    405 		if( attr.type == cgltf_attribute_type_position ) {
    406 			// TODO: if not animated, bake transform into mesh
    407 			mesh_config.positions = renderer_new_vb( AccessorToSpan( attr.data ) );
    408 
    409 			// XXX
    410 			array< v3 > colours = alloc_array< v3 >( arena, attr.data->count );
    411 			for( size_t k = 0; k < attr.data->count; k++ ) {
    412 				colours[ k ] = v3( 1, 1, 1 );
    413 			}
    414 			mesh_config.colours = renderer_new_vb( colours );
    415 		}
    416 
    417 		if( attr.type == cgltf_attribute_type_normal ) {
    418 			mesh_config.normals = renderer_new_vb( AccessorToSpan( attr.data ) );
    419 		}
    420 
    421 		if( attr.type == cgltf_attribute_type_texcoord ) {
    422 			mesh_config.tex_coords0 = renderer_new_vb( AccessorToSpan( attr.data ) );
    423 		}
    424 
    425 		if( attr.type == cgltf_attribute_type_color ) {
    426 			mesh_config.colours = renderer_new_vb( AccessorToSpan( attr.data ) );
    427 		}
    428 
    429 		// TODO: drop lanes that are never used?
    430 		// TODO: convert to u8
    431 		if( attr.type == cgltf_attribute_type_joints ) {
    432 			mesh_config.joints = renderer_new_vb( AccessorToSpan( attr.data ) );
    433 			if( attr.data->component_type == cgltf_component_type_r_8u )
    434 				mesh_config.joints_format = VERTEXFMT_U8x4;
    435 		}
    436 
    437 		// TODO: convert to float3 and compute the last weight in the shader
    438 		if( attr.type == cgltf_attribute_type_weights ) {
    439 			mesh_config.weights = renderer_new_vb( AccessorToSpan( attr.data ) );
    440 			if( attr.data->component_type == cgltf_component_type_r_8u )
    441 				mesh_config.weights_format = VERTEXFMT_U8x4;
    442 		}
    443 	}
    444 
    445 	mesh_config.indices = renderer_new_ib( AccessorToSpan( prim.indices ) );
    446 	mesh_config.indices_format = prim.indices->component_type == cgltf_component_type_r_16u ? INDEXFMT_U16 : INDEXFMT_U32;
    447 	mesh_config.num_vertices = prim.indices->count;
    448 
    449 	model.meshes[ model.num_meshes ] = renderer_new_mesh( mesh_config );
    450 	model.num_meshes++;
    451 }
    452 
    453 GAME_INIT( game_init ) {
    454 	memset( &model, 0, sizeof( model ) );
    455 
    456 	double before = get_time();
    457 
    458 	cgltf_options options = { };
    459 
    460 	cgltf_data * data;
    461 	if( cgltf_parse_file( &options, "bigvic.glb", &data ) != cgltf_result_success )
    462 		FATAL( "cgltf_parse_file" );
    463 	if( cgltf_load_buffers( &options, data, "./" ) != cgltf_result_success )
    464 		FATAL( "cgltf_load_buffers" );
    465 	if( !LoadBinaryBuffers( data ) )
    466 		FATAL( "LoadBinaryBuffers" );
    467 	if( cgltf_validate( data ) != cgltf_result_success )
    468 		FATAL( "cgltf_validate" );
    469 
    470 	ASSERT( data->animations_count <= 1 );
    471 	ASSERT( data->skins_count <= 1 );
    472 	ASSERT( data->animations_count == data->skins_count );
    473 	ASSERT( data->cameras_count == 0 );
    474 
    475 	model.num_meshes = 0;
    476 
    477 	for( size_t i = 0; i < data->scene->nodes_count; i++ ) {
    478 		LoadNode( &mem->persistent_arena, data->scene->nodes[ i ], data->animations_count > 0, 0 );
    479 	}
    480 
    481 	if( data->animations_count > 0 ) {
    482 		LoadSkin( &data->skins[ 0 ] );
    483 		LoadAnimationReel( &mem->persistent_arena, &data->animations[ 0 ] );
    484 		FixupMissingAnimationChannels( &data->skins[ 0 ] );
    485 	}
    486 
    487 	cgltf_free( data );
    488 
    489 	ggprint( "loading model took {.2}ms\n", ( get_time() - before ) * 1000.0 );
    490 
    491 	game->pos = v3( -10, -10, 5 );
    492 	game->pitch = 0;
    493 	game->yaw = 45;
    494 	game->sun_angle = 0;
    495 }
    496 
    497 GAME_FRAME( game_frame ) {
    498 	const float speed = 6.0f;
    499 	const float angular_speed = 100.0f;
    500 
    501 	float fb = float( input->keys[ KEY_W ] - input->keys[ KEY_S ] );
    502 	float lr = float( input->keys[ KEY_D ] - input->keys[ KEY_A ] );
    503 	float dz = float( input->keys[ KEY_SPACE ] - input->keys[ KEY_LEFTSHIFT ] );
    504 
    505 	float dpitch = float( input->keys[ KEY_DOWNARROW ] - input->keys[ KEY_UPARROW ] );
    506 	float dyaw = float( input->keys[ KEY_LEFTARROW ] - input->keys[ KEY_RIGHTARROW ] );
    507 
    508 	float dsun = ( input->keys[ KEY_EQUALS ] * input->key_edges[ KEY_EQUALS ] - input->keys[ KEY_MINUS ] * input->key_edges[ KEY_MINUS ] ) * 0.01666f;
    509 	dsun += input->keys[ KEY_T ] * 0.01666f;
    510 	game->sun_angle += dsun;
    511 
    512 	dpitch += input->keys[ KEY_K ] - input->keys[ KEY_I ];
    513 	dyaw += input->keys[ KEY_J ] - input->keys[ KEY_L ];
    514 
    515 	game->pitch += dpitch * dt * angular_speed;
    516 	game->yaw += dyaw * dt * angular_speed;
    517 
    518 	const v3 world_up = v3( 0, 0, 1 );
    519 	v3 forward = v3_forward( game->pitch, game->yaw );
    520 	v3 right = normalize( cross( forward, world_up ) );
    521 	v3 up = normalize( cross( right, forward ) );
    522 
    523 	game->pos += forward * dt * fb * speed;
    524 	game->pos += right * dt * lr * speed;
    525 	game->pos.z += dt * dz * speed;
    526 
    527 	m4 P = m4_perspective( 90, get_aspect_ratio(), NEAR_PLANE_DEPTH, FAR_PLANE_DEPTH );
    528 	m4 V = m4_view( forward, right, up, game->pos );
    529 
    530 	m4 y_up_to_z_up = m4(
    531 		1, 0, 0, 0,
    532 		0, 0, -1, 0,
    533 		0, 1, 0, 0,
    534 		0, 0, 0, 1
    535 	);
    536 
    537 	SQT sample_sqt[ 100 ];
    538 	SampleAnimationClip( model.animation, 0, game->sun_angle, 0.0f, sample_sqt );
    539 
    540 	Mat4 joint_matrices[ 100 ];
    541 	Mat4 skinning_matrices[ 100 ];
    542 	ComputeMatrixPalette( model.animation, sample_sqt, joint_matrices, skinning_matrices );
    543 
    544 	renderer_begin_frame();
    545 
    546 	u8 main_pass = renderer_add_pass( "Render frame", RENDERER_CLEAR_COLOUR_DO, RENDERER_CLEAR_DEPTH_DO );
    547 
    548 	UniformBinding view_uniforms = renderer_uniforms( V, P, game->pos );
    549 	UniformBinding model_uniforms = renderer_uniforms( y_up_to_z_up );
    550 	UniformBinding joints_uniforms = renderer_upload_uniforms( skinning_matrices, model.animation.num_joints * sizeof( Mat4 ) );
    551 
    552 	if( !input->keys[ KEY_LEFTCTRL ] ) {
    553 		// draw skinned model
    554 		{
    555 			RenderState render_state;
    556 			render_state.pass = main_pass;
    557 			render_state.shader = get_shader( SHADER_SKINNED_FLAT_VERTEX_COLOURS );
    558 			render_state.set_uniform( "view", view_uniforms );
    559 			render_state.set_uniform( "model", model_uniforms );
    560 			render_state.set_uniform( "animation", joints_uniforms );
    561 			render_state.wireframe = input->keys[ KEY_M ];
    562 
    563 			for( u32 i = 0; i < model.num_meshes; i++ ) {
    564 				renderer_draw_mesh( model.meshes[ i ], render_state );
    565 			}
    566 		}
    567 
    568 		// draw an arrow on the guy's head
    569 		{
    570 			u8 head_joint = 4;
    571 			v3 pos = ( y_up_to_z_up * joint_matrices[ head_joint ] * v4( 0, 0, 0, 1 ) ).xyz();
    572 			v3 up = ( y_up_to_z_up * joint_matrices[ head_joint ] * v4( 0, 1, 0, 0 ) ).xyz();
    573 
    574 			immediate_arrow( pos, up, 1, v4( 0, 1, 0, 1 ) );
    575 
    576 			RenderState render_state;
    577 			render_state.pass = main_pass;
    578 			render_state.shader = get_shader( SHADER_FLAT_VERTEX_COLOURS );
    579 			render_state.set_uniform( "view", renderer_uniforms( V, P ) );
    580 			immediate_render( render_state );
    581 		}
    582 	}
    583 	else {
    584 		// draw skeleton
    585 		for( u32 i = 0; i < model.animation.num_joints; i++ ) {
    586 			v3 pos = ( y_up_to_z_up * joint_matrices[ i ] * v4( 0, 0, 0, 1 ) ).xyz();
    587 			v3 parent_pos = ( y_up_to_z_up * joint_matrices[ model.animation.joints[ i ].parent ] * v4( 0, 0, 0, 1 ) ).xyz();
    588 
    589 			immediate_sphere( pos, 1.0, v4( 1 ) );
    590 			immediate_arrow( pos, ( y_up_to_z_up * joint_matrices[ i ].col1 ).xyz(), 5.0, v4( 0.5, 0.5, 0.5, 1 ) );
    591 			immediate_arrow( pos, ( y_up_to_z_up * joint_matrices[ i ].col0 ).xyz(), 5.0, v4( 0, 0, 1, 1 ) );
    592 			if( i != 0 )
    593 				immediate_arrow( pos, ( parent_pos - pos ), 0.1, v4( 1, 0, 0, 1 ) );
    594 		}
    595 
    596 		RenderState render_state;
    597 		render_state.pass = main_pass;
    598 		render_state.shader = get_shader( SHADER_FLAT_VERTEX_COLOURS );
    599 		render_state.set_uniform( "view", renderer_uniforms( V, P ) );
    600 		immediate_render( render_state );
    601 	}
    602 
    603 	renderer_end_frame();
    604 }