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
) )
278 vg_info( "player fell off due to glider hitting ground\n" );
279 player__dead_transition( k_player_die_type_generic
);
280 localplayer
.glider_orphan
= 1;
283 if( !world_water_player_safe( world_current_instance(), 1.0f
) )
287 void player_glide_post_update(void)
289 v3_copy( player_glide
.rb
.co
, localplayer
.rb
.co
);
290 v4_copy( player_glide
.rb
.q
, localplayer
.rb
.q
);
291 v3_copy( player_glide
.rb
.v
, localplayer
.rb
.v
);
292 v3_copy( player_glide
.rb
.w
, localplayer
.rb
.w
);
293 rb_update_matrices( &localplayer
.rb
);
296 void player_glide_animate(void)
298 struct player_glide
*g
= &player_glide
;
299 struct player_glide_animator
*animator
= &g
->animator
;
300 rb_extrapolate( &localplayer
.rb
, animator
->root_co
, animator
->root_q
);
303 void player_glide_pose( void *_animator
, player_pose
*pose
)
305 struct skeleton
*sk
= &localplayer
.skeleton
;
306 struct player_glide_animator
*animator
= _animator
;
307 pose
->type
= k_player_pose_type_ik
;
308 pose
->board
.lean
= 0.0f
;
310 skeleton_sample_anim( sk
, player_glide
.anim_glide
, 0.0f
, pose
->keyframes
);
313 q_mulv( animator
->root_q
, (v3f
){0,-0.5f
,0}, temp
);
314 v3_add( animator
->root_co
, temp
, pose
->root_co
);
316 v4_copy( animator
->root_q
, pose
->root_q
);
319 void player_glide_post_animate(void)
321 if( localplayer
.cam_control
.camera_mode
== k_cam_firstperson
)
322 localplayer
.cam_velocity_influence
= 0.0f
;
324 localplayer
.cam_velocity_influence
= 0.0f
;
327 v3_muls( localplayer
.rb
.to_world
[2], -1.0f
, fwd
);
328 v3_angles( fwd
, localplayer
.angles
);
330 localplayer
.cam_dist
= 2.0f
+ v3_length( localplayer
.rb
.v
)*0.2f
;
333 void player_glide_animator_exchange( bitpack_ctx
*ctx
, void *data
)
335 struct player_glide_animator
*animator
= data
;
337 bitpack_qv3f( ctx
, 24, -1024.0f
, 1024.0f
, animator
->root_co
);
338 bitpack_qquat( ctx
, animator
->root_q
);
341 void player_glide_remote_animator_exchange( bitpack_ctx
*ctx
, void *data
)
343 struct remote_glider_animator
*animator
= data
;
345 bitpack_qv3f( ctx
, 24, -1024.0f
, 1024.0f
, animator
->root_co
);
346 bitpack_qf32( ctx
, 8, 0.0f
, 1.0f
, &animator
->s
);
347 bitpack_qquat( ctx
, animator
->root_q
);
350 void player_glide_im_gui(void)
352 player__debugtext( 1, "Nothing here" );
353 player__debugtext( 1, " lift: %.2f %.2f %.2f",
354 player_glide
.info_lift
[0],
355 player_glide
.info_lift
[1],
356 player_glide
.info_lift
[2] );
357 player__debugtext( 1, " slip: %.2f %.2f %.2f",
358 player_glide
.info_slip
[0],
359 player_glide
.info_slip
[1],
360 player_glide
.info_slip
[2] );
361 player__debugtext( 1, " drag: %.2f %.2f %.2f",
362 player_glide
.info_drag
[0],
363 player_glide
.info_drag
[1],
364 player_glide
.info_drag
[2] );
367 void player_glide_equip_glider(void)
369 if( !localplayer
.have_glider
){
370 localplayer
.have_glider
= 1;
371 localplayer
.glider_orphan
= 0;
372 player_glide
.t
= -1.0f
;
376 static int ccmd_player_glider_spawn( int argc
, const char *argv
[] ){
377 if( vg_console
.cheats
){
378 player_glide_equip_glider();
381 vg_error( "Can't spawn without cheats enabled.\n" );
386 void player_glide_bind(void)
388 u32 mask
= VG_VAR_CHEAT
|VG_VAR_PERSISTENT
;
389 VG_VAR_F32( k_glide_steer
, flags
=mask
);
390 VG_VAR_F32( k_glide_cl
, flags
=mask
);
391 VG_VAR_F32( k_glide_cs
, flags
=mask
);
392 VG_VAR_F32( k_glide_drag
, flags
=mask
);
393 VG_VAR_F32( k_glide_slip_yaw
, flags
=mask
);
394 VG_VAR_F32( k_glide_lift_pitch
, flags
=mask
);
395 VG_VAR_I32( k_glide_pause
, flags
=mask
);
396 VG_VAR_F32( k_glide_balance
, flags
=mask
);
397 VG_VAR_F32( k_glide_wing_orient
, flags
=mask
);
399 vg_console_reg_cmd( "spawn_glider", ccmd_player_glider_spawn
, NULL
);
401 f32 mass
= 0.0f
,k_density
= 8.0f
;
405 for( u32 i
=0; i
<vg_list_size(player_glide
.parts
); i
++ ){
406 /* create part transform matrix */
408 q_axis_angle( qp
, (v3f
){1,0,0}, player_glide
.parts
[i
].euler
[0] );
409 q_axis_angle( qy
, (v3f
){0,1,0}, player_glide
.parts
[i
].euler
[1] );
410 q_axis_angle( qr
, (v3f
){0,0,1}, player_glide
.parts
[i
].euler
[2] );
415 q_m3x3( q
, player_glide
.parts
[i
].mdl
);
416 v3_copy( player_glide
.parts
[i
].co
, player_glide
.parts
[i
].mdl
[3] );
418 /* add it to inertia model */
420 if( player_glide
.parts
[i
].shape
== k_rb_shape_capsule
){
421 f32 r
= player_glide
.parts
[i
].inf
.r
,
422 h
= player_glide
.parts
[i
].inf
.h
,
423 pv
= vg_capsule_volume( r
, h
),
429 vg_capsule_inertia( r
, h
, pm
, pI
);
430 vg_rotate_inertia( pI
, player_glide
.parts
[i
].mdl
);
431 vg_translate_inertia( pI
, pm
, player_glide
.parts
[i
].co
);
432 m3x3_add( I
, pI
, I
);
434 else if( player_glide
.parts
[i
].shape
== k_rb_shape_sphere
){
435 f32 r
= player_glide
.parts
[i
].r
,
436 pv
= vg_sphere_volume( r
),
441 vg_sphere_inertia( r
, pm
, pI
);
442 vg_translate_inertia( pI
, pm
, player_glide
.parts
[i
].co
);
443 m3x3_add( I
, pI
, I
);
448 m3x3_inv( I
, player_glide
.rb
.iI
);
449 player_glide
.rb
.inv_mass
= 1.0f
/ mass
;
452 struct skeleton
*sk
= &localplayer
.skeleton
;
453 player_glide
.anim_glide
= skeleton_get_anim( sk
, "glide_pose" );
455 void *alloc
= vg_mem
.rtmemory
;
456 mdl_context
*mdl
= &player_glide
.glider
;
458 mdl_open( mdl
, "models/glider.mdl", alloc
);
459 mdl_load_metadata_block( mdl
, alloc
);
460 mdl_async_full_load_std( mdl
);
462 /* load trail positions */
463 mdl_array_ptr markers
;
464 MDL_LOAD_ARRAY( mdl
, &markers
, ent_marker
, vg_mem
.scratch
);
467 for( u32 i
=0; i
<mdl_arrcount( &markers
); i
++ )
469 ent_marker
*marker
= mdl_arritm( &markers
, i
);
470 v3_copy( marker
->transform
.co
,
471 player_glide
.trail_positions
[ player_glide
.trail_count
++ ] );
473 if( player_glide
.trail_count
== vg_list_size(trails_glider
) )
477 /* allocate effects */
478 for( u32 i
=0; i
<vg_list_size(trails_glider
); i
++ )
480 trail_alloc( &trails_glider
[i
], 200 );
484 void player_glide_transition(void)
486 localplayer
.subsystem
= k_player_subsystem_glide
;
487 localplayer
.have_glider
= 0;
488 world_static
.challenge_target
= NULL
;
489 world_static
.challenge_timer
= 0.0f
;
490 world_static
.focused_entity
= 0;
491 world_static
.last_use
= 0.0;
492 for( u32 i
=0; i
<vg_list_size(world_static
.instances
); i
++ ){
493 world_instance
*instance
= &world_static
.instances
[i
];
494 if( instance
->status
== k_world_status_loaded
){
495 world_routes_clear( instance
);
499 v3_copy( localplayer
.rb
.co
, player_glide
.rb
.co
);
501 f32 dir
= v3_dot( localplayer
.rb
.v
, localplayer
.rb
.to_world
[2] );
505 q_axis_angle( qyaw
, (v3f
){0,1,0}, VG_TAUf
*0.5f
);
506 q_mul( qyaw
, localplayer
.rb
.q
, player_glide
.rb
.q
);
507 q_normalize( player_glide
.rb
.q
);
510 v4_copy( localplayer
.rb
.q
, player_glide
.rb
.q
);
512 v3_copy( localplayer
.rb
.v
, player_glide
.rb
.v
);
513 v3_copy( localplayer
.rb
.w
, player_glide
.rb
.w
);
514 rb_update_matrices( &player_glide
.rb
);
516 player__begin_holdout( (v3f
){0,0,0} );
519 void render_glider_model( vg_camera
*cam
, world_instance
*world
,
520 m4x3f mmdl
, enum board_shader shader
)
522 u32 current_mat
= 0xffffffff;
523 glActiveTexture( GL_TEXTURE0
);
525 mdl_context
*mdl
= &player_glide
.glider
;
526 mesh_bind( &player_glide
.glider
.mesh
);
528 for( u32 i
=0; i
<mdl_arrcount(&mdl
->meshs
); i
++ )
530 mdl_mesh
*mesh
= mdl_arritm( &mdl
->meshs
, i
);
533 mdl_transform_m4x3( &mesh
->transform
, mmmdl
);
534 m4x3_mul( mmdl
, mmmdl
, mmmdl
);
536 if( shader
== k_board_shader_player
)
537 shader_model_board_view_uMdl( mmmdl
);
538 else if( shader
== k_board_shader_entity
)
541 m4x3_expand( mmmdl
, m4mmmdl
);
542 m4x4_mul( cam
->mtx_prev
.pv
, m4mmmdl
, m4mmmdl
);
544 shader_model_entity_uMdl( mmmdl
);
545 shader_model_entity_uPvmPrev( m4mmmdl
);
548 for( u32 j
=0; j
<mesh
->submesh_count
; j
++ )
550 mdl_submesh
*sm
= mdl_arritm( &mdl
->submeshs
, mesh
->submesh_start
+j
);
551 if( !sm
->material_id
)
553 vg_error( "Invalid material ID 0\n" );
557 if( sm
->material_id
!= current_mat
)
559 mdl_material
*mat
= mdl_arritm( &mdl
->materials
,sm
->material_id
-1 );
560 GLuint tex
= vg
.tex_missing
;
562 if( mat
->shader
== k_shader_standard
)
564 struct shader_props_standard
*props
= mat
->props
.compiled
;
566 u32 index
= props
->tex_diffuse
-1;
567 mdl_texture
*ptex
= mdl_arritm( &mdl
->textures
, index
);
571 glBindTexture( GL_TEXTURE_2D
, tex
);
572 current_mat
= sm
->material_id
;
575 mdl_draw_submesh( sm
);
581 * TODO: more flexible way to call
582 * - this depends on the current state, but we need to pass a struct in
583 * that can hold that information instead so we can save it into
586 void player_glide_render( vg_camera
*cam
, world_instance
*world
,
589 if( !((localplayer
.subsystem
== k_player_subsystem_glide
) ||
590 (localplayer
.observing_system
== k_player_subsystem_glide
) ||
591 localplayer
.have_glider
||
592 localplayer
.glider_orphan
) )
595 shader_model_board_view_use();
596 shader_model_board_view_uTexMain( 0 );
597 shader_model_board_view_uCamera( cam
->transform
[3] );
598 shader_model_board_view_uPv( cam
->mtx
.pv
);
599 shader_model_board_view_uDepthMode(1);
601 shader_model_board_view_uTexSceneDepth
,
602 shader_model_board_view_uInverseRatioDepth
,
603 shader_model_board_view_uInverseRatioMain
,
606 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, model_board_view
);
609 if( localplayer
.glider_orphan
){
610 rb_extrapolate( &player_glide
.rb
, kf_res
.co
, kf_res
.q
);
611 v3_fill( kf_res
.s
, 1.0f
);
614 q_mulv( kf_res
.q
, (v3f
){0,-0.5f
,0}, temp
);
615 v3_add( temp
, kf_res
.co
, kf_res
.co
);
619 if( localplayer
.subsystem
== k_player_subsystem_glide
) target
= 1.0f
;
623 if( skaterift
.activity
!= k_skaterift_replay
)
624 vg_slewf( &player_glide
.t
, target
, vg
.time_frame_delta
* 4.0f
);
626 mdl_keyframe kf_backpack
;
628 struct skeleton
*sk
= &localplayer
.skeleton
;
629 m4x3_mulv( localplayer
.final_mtx
[localplayer
.id_chest
],
630 sk
->bones
[localplayer
.id_chest
].co
,
633 v4f qyaw
, qpitch
, qchest
, q
;
634 q_axis_angle( qyaw
, (v3f
){0,1,0}, VG_TAUf
*0.25f
);
635 q_axis_angle( qpitch
, (v3f
){1,0,0}, VG_TAUf
*0.25f
);
636 m3x3_q( localplayer
.final_mtx
[ localplayer
.id_chest
], qchest
);
638 q_mul( qyaw
, qpitch
, q
);
639 q_mul( qchest
, q
, kf_backpack
.q
);
640 q_normalize( kf_backpack
.q
);
643 if( player_glide
.t
<= 0.0f
){
644 f32 st
= player_glide
.t
+ 1.0f
,
645 sst
= vg_smoothstepf(st
),
647 scale
= vg_lerpf( 0.0f
, 0.2f
, sst
);
650 q_axis_angle( qspin
, (v3f
){0,0,1}, VG_TAUf
* isst
* 0.5f
);
651 q_mul( kf_backpack
.q
, qspin
, kf_backpack
.q
);
652 kf_backpack
.co
[1] += isst
* 1.0f
;
653 v3_muladds( kf_backpack
.co
,
654 localplayer
.final_mtx
[ localplayer
.id_chest
][0],
659 scale
= vg_lerpf( 0.2f
, 1.0f
, vg_smoothstepf(player_glide
.t
) );
663 v3_fill( kf_backpack
.s
, scale
);
665 v3_copy( pose
->root_co
, kf_res
.co
);
666 v4_copy( pose
->root_q
, kf_res
.q
);
667 v3_fill( kf_res
.s
, scale
);
669 f32 blend
= vg_smoothstepf( vg_maxf( 0, player_glide
.t
) );
670 keyframe_lerp( &kf_backpack
, &kf_res
, blend
, &kf_res
);
674 q_m3x3( kf_res
.q
, mmdl
);
675 m3x3_scale( mmdl
, kf_res
.s
);
676 v3_copy( kf_res
.co
, mmdl
[3] );
678 render_glider_model( cam
, world
, mmdl
, k_board_shader_player
);
681 v4_copy( kf_res
.q
, player_glide
.remote_animator
.root_q
);
682 v3_copy( kf_res
.co
, player_glide
.remote_animator
.root_co
);
683 player_glide
.remote_animator
.s
= kf_res
.s
[0];
686 void player_glide_render_effects( vg_camera
*cam
)
690 rb_extrapolate( &player_glide
.rb
, co
, q
);
691 q_mulv( q
, (v3f
){0,-0.5f
,0}, temp
);
692 v3_add( temp
, co
, co
);
694 f32 alpha
= vg_maxf( (fabsf(player_glide
.info_lift
[2])-1.0f
), 0.0f
) /18.0f
;
696 for( u32 i
=0; i
<player_glide
.trail_count
; i
++ ){
698 q_mulv( q
, player_glide
.trail_positions
[i
], vvert
);
699 v3_add( co
, vvert
, vvert
);
701 trail_system_update( &trails_glider
[i
], vg
.time_delta
, vvert
,
702 localplayer
.rb
.to_world
[1], alpha
);
704 trail_system_prerender( &trails_glider
[i
] );
705 trail_system_render( &trails_glider
[i
], &skaterift
.cam
);