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 }