1 #include "player_glide.h"
2 #include "vg/vg_rigidbody.h"
3 #include "scene_rigidbody.h"
4 #include "shaders/model_board_view.h"
5 #include "shaders/model_entity.h"
9 #include "player_dead.h"
10 #include "player_skate.h"
12 trail_system trails_glider
[] = {
25 struct player_glide player_glide
=
29 .co
= { 1.0f
, 0.5f
, -1.0f
},
30 .euler
= { VG_TAUf
*0.25f
, VG_TAUf
*0.125f
, 0.0f
},
31 .shape
= k_rb_shape_capsule
,
32 .inf
= { .h
= 2.82842712475f
, .r
= 0.25f
},
35 .co
= { -1.0f
, 0.5f
, -1.0f
},
36 .euler
= { VG_TAUf
*0.25f
, -VG_TAUf
*0.125f
, 0.0f
},
37 .shape
= k_rb_shape_capsule
,
38 .inf
= { .h
= 2.82842712475f
, .r
= 0.25f
},
41 .co
= { 0.0f
, 0.5f
, 1.0f
},
42 .euler
= { VG_TAUf
*0.25f
, VG_TAUf
*0.25f
, 0.0f
},
43 .shape
= k_rb_shape_capsule
,
44 .inf
= { .h
= 6.0f
, .r
= 0.25f
},
47 .co
= { 0.0f
, -0.5f
, 0.0f
},
48 .euler
= { VG_TAUf
*0.25f
, VG_TAUf
*0.25f
, 0.0f
},
49 .shape
= k_rb_shape_capsule
,
50 .inf
= { .h
= 2.0f
, .r
= 0.25f
},
56 struct player_subsystem_interface player_subsystem_glide
=
58 .pre_update
= player_glide_pre_update
,
59 .update
= player_glide_update
,
60 .post_update
= player_glide_post_update
,
61 .animate
= player_glide_animate
,
62 .pose
= player_glide_pose
,
63 .post_animate
= player_glide_post_animate
,
64 .network_animator_exchange
= player_glide_animator_exchange
,
65 .im_gui
= player_glide_im_gui
,
66 .bind
= player_glide_bind
,
68 .animator_data
= &player_glide
.animator
,
69 .animator_size
= sizeof(player_glide
.animator
),
73 static f32 k_glide_steer
= 2.0f
,
76 k_glide_drag
= 0.0001f
,
77 k_glide_slip_yaw
= 0.1f
,
78 k_glide_lift_pitch
= 0.0f
,
79 k_glide_wing_orient
= -0.1f
,
80 k_glide_balance
= 1.0f
;
82 static i32 k_glide_pause
= 0;
84 void player_glide_pre_update(void)
86 if( button_down(k_srbind_use
) ){
87 localplayer
.subsystem
= k_player_subsystem_skate
;
88 localplayer
.glider_orphan
= 1;
90 player_skate
.state
.activity
= k_skate_activity_air
;
91 player_skate
.state
.activity_prev
= k_skate_activity_air
;
93 q_mulv( localplayer
.rb
.q
, (v3f
){0.0f
,1.0f
,0.0f
}, player_skate
.state
.cog
);
94 v3_add( player_skate
.state
.cog
, localplayer
.rb
.co
,
95 player_skate
.state
.cog
);
96 v3_copy( localplayer
.rb
.v
, player_skate
.state
.cog_v
);
98 player__begin_holdout( (v3f
){0.0f
,0.0f
,0.0f
} );
99 player__skate_reset_animator();
100 player__skate_clear_mechanics();
101 v3_copy( (v3f
){0.0f
,0.0f
,0.0f
}, player_skate
.state
.trick_euler
);
103 player__approximate_best_trajectory();
107 static void massless_accel( rigidbody
*rb
, v3f delta
, v3f impulse
){
109 v3_muladds( rb
->v
, impulse
, vg
.time_fixed_delta
, rb
->v
);
111 /* Angular velocity */
113 v3_cross( delta
, impulse
, wa
);
114 v3_muladds( rb
->w
, wa
, vg
.time_fixed_delta
, rb
->w
);
117 static void calculate_lift( v3f vl
, f32 aoa_bias
,
118 v3f axis
, v3f back
, f32 power
,
121 v3_cross( back
, axis
, up
);
124 v3_muladds( vl
, axis
, -v3_dot(axis
,vl
), wind
);
126 f32 windv2
= v3_length2(wind
),
127 aoa
= atan2f( v3_dot( up
, wind
), v3_dot( back
, wind
) ) + aoa_bias
,
129 L
= windv2
* cl
* power
;
132 v3_normalize( wind
);
133 v3_cross( wind
, axis
, lift_dir
);
135 /* this is where induced drag (from the flappy things) would go */
137 v3_muls( lift_dir
, L
, out_force
);
140 static void calculate_drag( v3f vl
, f32 cd
, v3f out_force
){
141 f32 v2
= v3_length2( vl
);
145 v3_muls( vl
, -cd
*v2
, out_force
);
149 * Returns true if the bottom sphere is hit
151 bool glider_physics( v2f steer
)
153 rigidbody
*rb
= &player_glide
.rb
;
157 m3x3_mulv( rb
->to_local
, rb
->v
, vl
);
158 m3x3_mulv( rb
->to_local
, rb
->w
, wl
);
160 v3f F
, Flift
, Fslip
, Fdrag
, FslipW
, FliftW
;
162 calculate_lift( vl
, steer
[1]*k_glide_steer
,
164 (v3f
){0,sinf(k_glide_wing_orient
),cosf(k_glide_wing_orient
)},
166 v3_copy( Flift
, player_glide
.info_lift
);
167 v3_cross( (v3f
){0,0,0}, Flift
, FliftW
);
169 calculate_lift( vl
, 0.0f
,
170 (v3f
){0,1,0},(v3f
){0,0,1},
172 v3_copy( Fslip
, player_glide
.info_slip
);
173 v3_cross( (v3f
){0,k_glide_lift_pitch
,k_glide_slip_yaw
}, Fslip
, FslipW
);
175 calculate_drag( vl
, k_glide_drag
, Fdrag
);
176 v3_copy( Fdrag
, player_glide
.info_drag
);
178 v3f balance
= {0.0f
,-k_glide_balance
,0.0f
};
179 m3x3_mulv( rb
->to_local
, balance
, balance
);
182 steer
[1]*k_glide_steer
- balance
[2],
184 -steer
[0]*k_glide_steer
+ balance
[0],
187 if( player_glide
.ticker
){
188 player_glide
.ticker
--;
191 player_glide
.ticker
+= k_glide_pause
;
194 v3_add( Flift
, Fslip
, F
);
195 v3_add( F
, Fdrag
, F
);
197 m3x3_mulv( rb
->to_world
, F
, F
);
198 v3_muladds( rb
->v
, F
, vg
.time_fixed_delta
, rb
->v
);
200 v3_add( Fw
, FslipW
, Fw
);
201 v3_add( Fw
, FliftW
, Fw
);
202 m3x3_mulv( rb
->to_world
, Fw
, Fw
);
203 v3_muladds( rb
->w
, Fw
, vg
.time_fixed_delta
, rb
->w
);
207 * collisions & constraints
209 world_instance
*world
= world_current_instance();
214 rigidbody _null
= {0};
215 _null
.inv_mass
= 0.0f
;
216 m3x3_zero( _null
.iI
);
217 for( u32 i
=0; i
< vg_list_size(player_glide
.parts
); i
++ ){
219 m4x3_mul( rb
->to_world
, player_glide
.parts
[i
].mdl
, mmdl
);
221 if( player_glide
.parts
[i
].shape
== k_rb_shape_capsule
){
222 vg_line_capsule( mmdl
,
223 player_glide
.parts
[i
].inf
.r
,
224 player_glide
.parts
[i
].inf
.h
,
227 else if( player_glide
.parts
[i
].shape
== k_rb_shape_sphere
){
228 vg_line_sphere( mmdl
, player_glide
.parts
[i
].r
, 0 );
231 if( rb_global_has_space() ){
232 rb_ct
*buf
= rb_global_buffer();
236 if( player_glide
.parts
[i
].shape
== k_rb_shape_capsule
){
237 l
= rb_capsule__scene( mmdl
, &player_glide
.parts
[i
].inf
,
238 NULL
, world
->geo_bh
, buf
,
239 k_material_flag_ghosts
);
241 else if( player_glide
.parts
[i
].shape
== k_rb_shape_sphere
){
242 l
= rb_sphere__scene( mmdl
, player_glide
.parts
[i
].r
,
243 NULL
, world
->geo_bh
, buf
,
244 k_material_flag_ghosts
);
247 if( player_glide
.parts
[i
].is_damage
&& l
){
251 for( u32 j
=0; j
<l
; j
++ ){
256 rb_contact_count
+= l
;
260 rb_presolve_contacts( rb_contact_buffer
,
261 vg
.time_fixed_delta
, rb_contact_count
);
262 for( u32 i
=0; i
<10; i
++ )
263 rb_solve_contacts( rb_contact_buffer
, rb_contact_count
);
266 rb_update_matrices( rb
);
271 void player_glide_update(void)
274 joystick_state( k_srjoystick_steer
, steer
);
276 if( glider_physics( steer
) ){
277 vg_info( "player fell off due to glider hitting ground\n" );
278 player__dead_transition( k_player_die_type_generic
);
279 localplayer
.glider_orphan
= 1;
283 void player_glide_post_update(void)
285 v3_copy( player_glide
.rb
.co
, localplayer
.rb
.co
);
286 v4_copy( player_glide
.rb
.q
, localplayer
.rb
.q
);
287 v3_copy( player_glide
.rb
.v
, localplayer
.rb
.v
);
288 v3_copy( player_glide
.rb
.w
, localplayer
.rb
.w
);
289 rb_update_matrices( &localplayer
.rb
);
292 void player_glide_animate(void)
294 struct player_glide
*g
= &player_glide
;
295 struct player_glide_animator
*animator
= &g
->animator
;
296 rb_extrapolate( &localplayer
.rb
, animator
->root_co
, animator
->root_q
);
299 void player_glide_pose( void *_animator
, player_pose
*pose
)
301 struct skeleton
*sk
= &localplayer
.skeleton
;
302 struct player_glide_animator
*animator
= _animator
;
303 pose
->type
= k_player_pose_type_ik
;
304 pose
->board
.lean
= 0.0f
;
306 skeleton_sample_anim( sk
, player_glide
.anim_glide
, 0.0f
, pose
->keyframes
);
309 q_mulv( animator
->root_q
, (v3f
){0,-0.5f
,0}, temp
);
310 v3_add( animator
->root_co
, temp
, pose
->root_co
);
312 v4_copy( animator
->root_q
, pose
->root_q
);
315 void player_glide_post_animate(void)
317 if( localplayer
.cam_control
.camera_mode
== k_cam_firstperson
)
318 localplayer
.cam_velocity_influence
= 0.0f
;
320 localplayer
.cam_velocity_influence
= 0.0f
;
323 v3_muls( localplayer
.rb
.to_world
[2], -1.0f
, fwd
);
324 v3_angles( fwd
, localplayer
.angles
);
326 localplayer
.cam_dist
= 2.0f
+ v3_length( localplayer
.rb
.v
)*0.2f
;
329 void player_glide_animator_exchange( bitpack_ctx
*ctx
, void *data
)
331 struct player_glide_animator
*animator
= data
;
333 bitpack_qv3f( ctx
, 24, -1024.0f
, 1024.0f
, animator
->root_co
);
334 bitpack_qquat( ctx
, animator
->root_q
);
337 void player_glide_remote_animator_exchange( bitpack_ctx
*ctx
, void *data
)
339 struct remote_glider_animator
*animator
= data
;
341 bitpack_qv3f( ctx
, 24, -1024.0f
, 1024.0f
, animator
->root_co
);
342 bitpack_qf32( ctx
, 8, 0.0f
, 1.0f
, &animator
->s
);
343 bitpack_qquat( ctx
, animator
->root_q
);
346 void player_glide_im_gui(void)
348 player__debugtext( 1, "Nothing here" );
349 player__debugtext( 1, " lift: %.2f %.2f %.2f",
350 player_glide
.info_lift
[0],
351 player_glide
.info_lift
[1],
352 player_glide
.info_lift
[2] );
353 player__debugtext( 1, " slip: %.2f %.2f %.2f",
354 player_glide
.info_slip
[0],
355 player_glide
.info_slip
[1],
356 player_glide
.info_slip
[2] );
357 player__debugtext( 1, " drag: %.2f %.2f %.2f",
358 player_glide
.info_drag
[0],
359 player_glide
.info_drag
[1],
360 player_glide
.info_drag
[2] );
363 void player_glide_equip_glider(void)
365 if( !localplayer
.have_glider
){
366 localplayer
.have_glider
= 1;
367 localplayer
.glider_orphan
= 0;
368 player_glide
.t
= -1.0f
;
372 static int ccmd_player_glider_spawn( int argc
, const char *argv
[] ){
373 if( vg_console
.cheats
){
374 player_glide_equip_glider();
377 vg_error( "Can't spawn without cheats enabled.\n" );
382 void player_glide_bind(void)
384 u32 mask
= VG_VAR_CHEAT
|VG_VAR_PERSISTENT
;
385 VG_VAR_F32( k_glide_steer
, flags
=mask
);
386 VG_VAR_F32( k_glide_cl
, flags
=mask
);
387 VG_VAR_F32( k_glide_cs
, flags
=mask
);
388 VG_VAR_F32( k_glide_drag
, flags
=mask
);
389 VG_VAR_F32( k_glide_slip_yaw
, flags
=mask
);
390 VG_VAR_F32( k_glide_lift_pitch
, flags
=mask
);
391 VG_VAR_I32( k_glide_pause
, flags
=mask
);
392 VG_VAR_F32( k_glide_balance
, flags
=mask
);
393 VG_VAR_F32( k_glide_wing_orient
, flags
=mask
);
395 vg_console_reg_cmd( "spawn_glider", ccmd_player_glider_spawn
, NULL
);
397 f32 mass
= 0.0f
,k_density
= 8.0f
;
401 for( u32 i
=0; i
<vg_list_size(player_glide
.parts
); i
++ ){
402 /* create part transform matrix */
404 q_axis_angle( qp
, (v3f
){1,0,0}, player_glide
.parts
[i
].euler
[0] );
405 q_axis_angle( qy
, (v3f
){0,1,0}, player_glide
.parts
[i
].euler
[1] );
406 q_axis_angle( qr
, (v3f
){0,0,1}, player_glide
.parts
[i
].euler
[2] );
411 q_m3x3( q
, player_glide
.parts
[i
].mdl
);
412 v3_copy( player_glide
.parts
[i
].co
, player_glide
.parts
[i
].mdl
[3] );
414 /* add it to inertia model */
416 if( player_glide
.parts
[i
].shape
== k_rb_shape_capsule
){
417 f32 r
= player_glide
.parts
[i
].inf
.r
,
418 h
= player_glide
.parts
[i
].inf
.h
,
419 pv
= vg_capsule_volume( r
, h
),
425 vg_capsule_inertia( r
, h
, pm
, pI
);
426 vg_rotate_inertia( pI
, player_glide
.parts
[i
].mdl
);
427 vg_translate_inertia( pI
, pm
, player_glide
.parts
[i
].co
);
428 m3x3_add( I
, pI
, I
);
430 else if( player_glide
.parts
[i
].shape
== k_rb_shape_sphere
){
431 f32 r
= player_glide
.parts
[i
].r
,
432 pv
= vg_sphere_volume( r
),
437 vg_sphere_inertia( r
, pm
, pI
);
438 vg_translate_inertia( pI
, pm
, player_glide
.parts
[i
].co
);
439 m3x3_add( I
, pI
, I
);
444 m3x3_inv( I
, player_glide
.rb
.iI
);
445 player_glide
.rb
.inv_mass
= 1.0f
/ mass
;
448 struct skeleton
*sk
= &localplayer
.skeleton
;
449 player_glide
.anim_glide
= skeleton_get_anim( sk
, "glide_pose" );
451 void *alloc
= vg_mem
.rtmemory
;
452 mdl_context
*mdl
= &player_glide
.glider
;
454 mdl_open( mdl
, "models/glider.mdl", alloc
);
455 mdl_load_metadata_block( mdl
, alloc
);
456 mdl_async_full_load_std( mdl
);
458 /* load trail positions */
459 mdl_array_ptr markers
;
460 MDL_LOAD_ARRAY( mdl
, &markers
, ent_marker
, vg_mem
.scratch
);
463 for( u32 i
=0; i
<mdl_arrcount( &markers
); i
++ )
465 ent_marker
*marker
= mdl_arritm( &markers
, i
);
466 v3_copy( marker
->transform
.co
,
467 player_glide
.trail_positions
[ player_glide
.trail_count
++ ] );
469 if( player_glide
.trail_count
== vg_list_size(trails_glider
) )
473 /* allocate effects */
474 for( u32 i
=0; i
<vg_list_size(trails_glider
); i
++ )
476 trail_alloc( &trails_glider
[i
], 200 );
480 void player_glide_transition(void)
482 localplayer
.subsystem
= k_player_subsystem_glide
;
483 localplayer
.have_glider
= 0;
484 world_static
.challenge_target
= NULL
;
485 world_static
.challenge_timer
= 0.0f
;
486 world_static
.focused_entity
= 0;
487 world_static
.last_use
= 0.0;
488 for( u32 i
=0; i
<vg_list_size(world_static
.instances
); i
++ ){
489 world_instance
*instance
= &world_static
.instances
[i
];
490 if( instance
->status
== k_world_status_loaded
){
491 world_routes_clear( instance
);
495 v3_copy( localplayer
.rb
.co
, player_glide
.rb
.co
);
497 f32 dir
= v3_dot( localplayer
.rb
.v
, localplayer
.rb
.to_world
[2] );
501 q_axis_angle( qyaw
, (v3f
){0,1,0}, VG_TAUf
*0.5f
);
502 q_mul( qyaw
, localplayer
.rb
.q
, player_glide
.rb
.q
);
503 q_normalize( player_glide
.rb
.q
);
506 v4_copy( localplayer
.rb
.q
, player_glide
.rb
.q
);
508 v3_copy( localplayer
.rb
.v
, player_glide
.rb
.v
);
509 v3_copy( localplayer
.rb
.w
, player_glide
.rb
.w
);
510 rb_update_matrices( &player_glide
.rb
);
512 player__begin_holdout( (v3f
){0,0,0} );
515 void render_glider_model( vg_camera
*cam
, world_instance
*world
,
516 m4x3f mmdl
, enum board_shader shader
)
518 u32 current_tex
= 0xffffffff;
519 glActiveTexture( GL_TEXTURE0
);
521 mdl_context
*mdl
= &player_glide
.glider
;
522 mesh_bind( &player_glide
.glider
.mesh
);
524 for( u32 i
=0; i
<mdl_arrcount(&mdl
->meshs
); i
++ )
526 mdl_mesh
*mesh
= mdl_arritm( &mdl
->meshs
, i
);
529 mdl_transform_m4x3( &mesh
->transform
, mmmdl
);
530 m4x3_mul( mmdl
, mmmdl
, mmmdl
);
532 if( shader
== k_board_shader_player
)
533 shader_model_board_view_uMdl( mmmdl
);
534 else if( shader
== k_board_shader_entity
)
537 m4x3_expand( mmmdl
, m4mmmdl
);
538 m4x4_mul( cam
->mtx_prev
.pv
, m4mmmdl
, m4mmmdl
);
540 shader_model_entity_uMdl( mmmdl
);
541 shader_model_entity_uPvmPrev( m4mmmdl
);
544 for( u32 j
=0; j
<mesh
->submesh_count
; j
++ )
546 mdl_submesh
*sm
= mdl_arritm( &mdl
->submeshs
, mesh
->submesh_start
+j
);
547 if( !sm
->material_id
)
549 vg_error( "Invalid material ID 0\n" );
553 mdl_material
*mat
= mdl_arritm( &mdl
->materials
, sm
->material_id
-1 );
554 if( mat
->tex_diffuse
!= current_tex
)
556 GLuint tex
= vg
.tex_missing
;
558 if( mat
->tex_diffuse
)
560 u32 index
= mat
->tex_diffuse
-1;
561 mdl_texture
*ptex
= mdl_arritm( &mdl
->textures
, index
);
565 glBindTexture( GL_TEXTURE_2D
, tex
);
566 current_tex
= mat
->tex_diffuse
;
569 mdl_draw_submesh( sm
);
575 * TODO: more flexible way to call
576 * - this depends on the current state, but we need to pass a struct in
577 * that can hold that information instead so we can save it into
580 void player_glide_render( vg_camera
*cam
, world_instance
*world
,
583 if( !((localplayer
.subsystem
== k_player_subsystem_glide
) ||
584 (localplayer
.observing_system
== k_player_subsystem_glide
) ||
585 localplayer
.have_glider
||
586 localplayer
.glider_orphan
) )
589 shader_model_board_view_use();
590 shader_model_board_view_uTexMain( 0 );
591 shader_model_board_view_uCamera( cam
->transform
[3] );
592 shader_model_board_view_uPv( cam
->mtx
.pv
);
593 shader_model_board_view_uDepthMode(1);
595 shader_model_board_view_uTexSceneDepth
,
596 shader_model_board_view_uInverseRatioDepth
,
597 shader_model_board_view_uInverseRatioMain
,
600 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, model_board_view
);
603 if( localplayer
.glider_orphan
){
604 rb_extrapolate( &player_glide
.rb
, kf_res
.co
, kf_res
.q
);
605 v3_fill( kf_res
.s
, 1.0f
);
608 q_mulv( kf_res
.q
, (v3f
){0,-0.5f
,0}, temp
);
609 v3_add( temp
, kf_res
.co
, kf_res
.co
);
613 if( localplayer
.subsystem
== k_player_subsystem_glide
) target
= 1.0f
;
617 if( skaterift
.activity
!= k_skaterift_replay
)
618 vg_slewf( &player_glide
.t
, target
, vg
.time_frame_delta
* 4.0f
);
620 mdl_keyframe kf_backpack
;
622 struct skeleton
*sk
= &localplayer
.skeleton
;
623 m4x3_mulv( localplayer
.final_mtx
[localplayer
.id_chest
],
624 sk
->bones
[localplayer
.id_chest
].co
,
627 v4f qyaw
, qpitch
, qchest
, q
;
628 q_axis_angle( qyaw
, (v3f
){0,1,0}, VG_TAUf
*0.25f
);
629 q_axis_angle( qpitch
, (v3f
){1,0,0}, VG_TAUf
*0.25f
);
630 m3x3_q( localplayer
.final_mtx
[ localplayer
.id_chest
], qchest
);
632 q_mul( qyaw
, qpitch
, q
);
633 q_mul( qchest
, q
, kf_backpack
.q
);
634 q_normalize( kf_backpack
.q
);
637 if( player_glide
.t
<= 0.0f
){
638 f32 st
= player_glide
.t
+ 1.0f
,
639 sst
= vg_smoothstepf(st
),
641 scale
= vg_lerpf( 0.0f
, 0.2f
, sst
);
644 q_axis_angle( qspin
, (v3f
){0,0,1}, VG_TAUf
* isst
* 0.5f
);
645 q_mul( kf_backpack
.q
, qspin
, kf_backpack
.q
);
646 kf_backpack
.co
[1] += isst
* 1.0f
;
647 v3_muladds( kf_backpack
.co
,
648 localplayer
.final_mtx
[ localplayer
.id_chest
][0],
653 scale
= vg_lerpf( 0.2f
, 1.0f
, vg_smoothstepf(player_glide
.t
) );
657 v3_fill( kf_backpack
.s
, scale
);
659 v3_copy( pose
->root_co
, kf_res
.co
);
660 v4_copy( pose
->root_q
, kf_res
.q
);
661 v3_fill( kf_res
.s
, scale
);
663 f32 blend
= vg_smoothstepf( vg_maxf( 0, player_glide
.t
) );
664 keyframe_lerp( &kf_backpack
, &kf_res
, blend
, &kf_res
);
668 q_m3x3( kf_res
.q
, mmdl
);
669 m3x3_scale( mmdl
, kf_res
.s
);
670 v3_copy( kf_res
.co
, mmdl
[3] );
672 render_glider_model( cam
, world
, mmdl
, k_board_shader_player
);
675 v4_copy( kf_res
.q
, player_glide
.remote_animator
.root_q
);
676 v3_copy( kf_res
.co
, player_glide
.remote_animator
.root_co
);
677 player_glide
.remote_animator
.s
= kf_res
.s
[0];
680 void player_glide_render_effects( vg_camera
*cam
)
684 rb_extrapolate( &player_glide
.rb
, co
, q
);
685 q_mulv( q
, (v3f
){0,-0.5f
,0}, temp
);
686 v3_add( temp
, co
, co
);
688 f32 alpha
= vg_maxf( (fabsf(player_glide
.info_lift
[2])-1.0f
), 0.0f
) /18.0f
;
690 for( u32 i
=0; i
<player_glide
.trail_count
; i
++ ){
692 q_mulv( q
, player_glide
.trail_positions
[i
], vvert
);
693 v3_add( co
, vvert
, vvert
);
695 trail_system_update( &trails_glider
[i
], vg
.time_delta
, vvert
,
696 localplayer
.rb
.to_world
[1], alpha
);
698 trail_system_prerender( &trails_glider
[i
] );
699 trail_system_render( &trails_glider
[i
], &skaterift
.cam
);