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( ui_context
*ctx
)
352 player__debugtext( ctx
, 1, " lift: %.2f %.2f %.2f",
353 player_glide
.info_lift
[0],
354 player_glide
.info_lift
[1],
355 player_glide
.info_lift
[2] );
356 player__debugtext( ctx
, 1, " slip: %.2f %.2f %.2f",
357 player_glide
.info_slip
[0],
358 player_glide
.info_slip
[1],
359 player_glide
.info_slip
[2] );
360 player__debugtext( ctx
, 1, " drag: %.2f %.2f %.2f",
361 player_glide
.info_drag
[0],
362 player_glide
.info_drag
[1],
363 player_glide
.info_drag
[2] );
366 void player_glide_equip_glider(void)
368 if( !localplayer
.have_glider
){
369 localplayer
.have_glider
= 1;
370 localplayer
.glider_orphan
= 0;
371 player_glide
.t
= -1.0f
;
375 static int ccmd_player_glider_spawn( int argc
, const char *argv
[] ){
376 if( vg_console
.cheats
){
377 player_glide_equip_glider();
380 vg_error( "Can't spawn without cheats enabled.\n" );
385 void player_glide_bind(void)
387 u32 mask
= VG_VAR_CHEAT
|VG_VAR_PERSISTENT
;
388 VG_VAR_F32( k_glide_steer
, flags
=mask
);
389 VG_VAR_F32( k_glide_cl
, flags
=mask
);
390 VG_VAR_F32( k_glide_cs
, flags
=mask
);
391 VG_VAR_F32( k_glide_drag
, flags
=mask
);
392 VG_VAR_F32( k_glide_slip_yaw
, flags
=mask
);
393 VG_VAR_F32( k_glide_lift_pitch
, flags
=mask
);
394 VG_VAR_I32( k_glide_pause
, flags
=mask
);
395 VG_VAR_F32( k_glide_balance
, flags
=mask
);
396 VG_VAR_F32( k_glide_wing_orient
, flags
=mask
);
398 vg_console_reg_cmd( "spawn_glider", ccmd_player_glider_spawn
, NULL
);
400 f32 mass
= 0.0f
,k_density
= 8.0f
;
404 for( u32 i
=0; i
<vg_list_size(player_glide
.parts
); i
++ ){
405 /* create part transform matrix */
407 q_axis_angle( qp
, (v3f
){1,0,0}, player_glide
.parts
[i
].euler
[0] );
408 q_axis_angle( qy
, (v3f
){0,1,0}, player_glide
.parts
[i
].euler
[1] );
409 q_axis_angle( qr
, (v3f
){0,0,1}, player_glide
.parts
[i
].euler
[2] );
414 q_m3x3( q
, player_glide
.parts
[i
].mdl
);
415 v3_copy( player_glide
.parts
[i
].co
, player_glide
.parts
[i
].mdl
[3] );
417 /* add it to inertia model */
419 if( player_glide
.parts
[i
].shape
== k_rb_shape_capsule
){
420 f32 r
= player_glide
.parts
[i
].inf
.r
,
421 h
= player_glide
.parts
[i
].inf
.h
,
422 pv
= vg_capsule_volume( r
, h
),
428 vg_capsule_inertia( r
, h
, pm
, pI
);
429 vg_rotate_inertia( pI
, player_glide
.parts
[i
].mdl
);
430 vg_translate_inertia( pI
, pm
, player_glide
.parts
[i
].co
);
431 m3x3_add( I
, pI
, I
);
433 else if( player_glide
.parts
[i
].shape
== k_rb_shape_sphere
){
434 f32 r
= player_glide
.parts
[i
].r
,
435 pv
= vg_sphere_volume( r
),
440 vg_sphere_inertia( r
, pm
, pI
);
441 vg_translate_inertia( pI
, pm
, player_glide
.parts
[i
].co
);
442 m3x3_add( I
, pI
, I
);
447 m3x3_inv( I
, player_glide
.rb
.iI
);
448 player_glide
.rb
.inv_mass
= 1.0f
/ mass
;
451 struct skeleton
*sk
= &localplayer
.skeleton
;
452 player_glide
.anim_glide
= skeleton_get_anim( sk
, "glide_pose" );
454 void *alloc
= vg_mem
.rtmemory
;
455 mdl_context
*mdl
= &player_glide
.glider
;
457 mdl_open( mdl
, "models/glider.mdl", alloc
);
458 mdl_load_metadata_block( mdl
, alloc
);
459 mdl_async_full_load_std( mdl
);
461 /* load trail positions */
462 mdl_array_ptr markers
;
463 MDL_LOAD_ARRAY( mdl
, &markers
, ent_marker
, vg_mem
.scratch
);
466 for( u32 i
=0; i
<mdl_arrcount( &markers
); i
++ )
468 ent_marker
*marker
= mdl_arritm( &markers
, i
);
469 v3_copy( marker
->transform
.co
,
470 player_glide
.trail_positions
[ player_glide
.trail_count
++ ] );
472 if( player_glide
.trail_count
== vg_list_size(trails_glider
) )
476 /* allocate effects */
477 for( u32 i
=0; i
<vg_list_size(trails_glider
); i
++ )
479 trail_alloc( &trails_glider
[i
], 200 );
483 void player_glide_transition(void)
485 localplayer
.subsystem
= k_player_subsystem_glide
;
486 localplayer
.have_glider
= 0;
487 world_static
.challenge_target
= NULL
;
488 world_static
.challenge_timer
= 0.0f
;
489 world_static
.focused_entity
= 0;
490 world_static
.last_use
= 0.0;
491 for( u32 i
=0; i
<vg_list_size(world_static
.instances
); i
++ ){
492 world_instance
*instance
= &world_static
.instances
[i
];
493 if( instance
->status
== k_world_status_loaded
){
494 world_routes_clear( instance
);
498 v3_copy( localplayer
.rb
.co
, player_glide
.rb
.co
);
500 f32 dir
= v3_dot( localplayer
.rb
.v
, localplayer
.rb
.to_world
[2] );
504 q_axis_angle( qyaw
, (v3f
){0,1,0}, VG_TAUf
*0.5f
);
505 q_mul( qyaw
, localplayer
.rb
.q
, player_glide
.rb
.q
);
506 q_normalize( player_glide
.rb
.q
);
509 v4_copy( localplayer
.rb
.q
, player_glide
.rb
.q
);
511 v3_copy( localplayer
.rb
.v
, player_glide
.rb
.v
);
512 v3_copy( localplayer
.rb
.w
, player_glide
.rb
.w
);
513 rb_update_matrices( &player_glide
.rb
);
515 player__begin_holdout( (v3f
){0,0,0} );
518 void render_glider_model( vg_camera
*cam
, world_instance
*world
,
519 m4x3f mmdl
, enum board_shader shader
)
521 u32 current_mat
= 0xffffffff;
522 glActiveTexture( GL_TEXTURE0
);
524 mdl_context
*mdl
= &player_glide
.glider
;
525 mesh_bind( &player_glide
.glider
.mesh
);
527 for( u32 i
=0; i
<mdl_arrcount(&mdl
->meshs
); i
++ )
529 mdl_mesh
*mesh
= mdl_arritm( &mdl
->meshs
, i
);
532 mdl_transform_m4x3( &mesh
->transform
, mmmdl
);
533 m4x3_mul( mmdl
, mmmdl
, mmmdl
);
535 if( shader
== k_board_shader_player
)
536 shader_model_board_view_uMdl( mmmdl
);
537 else if( shader
== k_board_shader_entity
)
540 m4x3_expand( mmmdl
, m4mmmdl
);
541 m4x4_mul( cam
->mtx_prev
.pv
, m4mmmdl
, m4mmmdl
);
543 shader_model_entity_uMdl( mmmdl
);
544 shader_model_entity_uPvmPrev( m4mmmdl
);
547 for( u32 j
=0; j
<mesh
->submesh_count
; j
++ )
549 mdl_submesh
*sm
= mdl_arritm( &mdl
->submeshs
, mesh
->submesh_start
+j
);
550 if( !sm
->material_id
)
552 vg_error( "Invalid material ID 0\n" );
556 if( sm
->material_id
!= current_mat
)
558 mdl_material
*mat
= mdl_arritm( &mdl
->materials
,sm
->material_id
-1 );
559 GLuint tex
= vg
.tex_missing
;
561 if( mat
->shader
== k_shader_standard
)
563 struct shader_props_standard
*props
= mat
->props
.compiled
;
565 u32 index
= props
->tex_diffuse
-1;
566 mdl_texture
*ptex
= mdl_arritm( &mdl
->textures
, index
);
570 glBindTexture( GL_TEXTURE_2D
, tex
);
571 current_mat
= sm
->material_id
;
574 mdl_draw_submesh( sm
);
580 * TODO: more flexible way to call
581 * - this depends on the current state, but we need to pass a struct in
582 * that can hold that information instead so we can save it into
585 void player_glide_render( vg_camera
*cam
, world_instance
*world
,
588 if( !((localplayer
.subsystem
== k_player_subsystem_glide
) ||
589 (localplayer
.observing_system
== k_player_subsystem_glide
) ||
590 localplayer
.have_glider
||
591 localplayer
.glider_orphan
) )
594 shader_model_board_view_use();
595 shader_model_board_view_uTexMain( 0 );
596 shader_model_board_view_uCamera( cam
->transform
[3] );
597 shader_model_board_view_uPv( cam
->mtx
.pv
);
598 shader_model_board_view_uDepthMode(1);
600 shader_model_board_view_uTexSceneDepth
,
601 shader_model_board_view_uInverseRatioDepth
,
602 shader_model_board_view_uInverseRatioMain
,
605 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, model_board_view
);
608 if( localplayer
.glider_orphan
){
609 rb_extrapolate( &player_glide
.rb
, kf_res
.co
, kf_res
.q
);
610 v3_fill( kf_res
.s
, 1.0f
);
613 q_mulv( kf_res
.q
, (v3f
){0,-0.5f
,0}, temp
);
614 v3_add( temp
, kf_res
.co
, kf_res
.co
);
618 if( localplayer
.subsystem
== k_player_subsystem_glide
) target
= 1.0f
;
622 if( skaterift
.activity
!= k_skaterift_replay
)
623 vg_slewf( &player_glide
.t
, target
, vg
.time_frame_delta
* 4.0f
);
625 mdl_keyframe kf_backpack
;
627 struct skeleton
*sk
= &localplayer
.skeleton
;
628 m4x3_mulv( localplayer
.final_mtx
[localplayer
.id_chest
],
629 sk
->bones
[localplayer
.id_chest
].co
,
632 v4f qyaw
, qpitch
, qchest
, q
;
633 q_axis_angle( qyaw
, (v3f
){0,1,0}, VG_TAUf
*0.25f
);
634 q_axis_angle( qpitch
, (v3f
){1,0,0}, VG_TAUf
*0.25f
);
635 m3x3_q( localplayer
.final_mtx
[ localplayer
.id_chest
], qchest
);
637 q_mul( qyaw
, qpitch
, q
);
638 q_mul( qchest
, q
, kf_backpack
.q
);
639 q_normalize( kf_backpack
.q
);
642 if( player_glide
.t
<= 0.0f
){
643 f32 st
= player_glide
.t
+ 1.0f
,
644 sst
= vg_smoothstepf(st
),
646 scale
= vg_lerpf( 0.0f
, 0.2f
, sst
);
649 q_axis_angle( qspin
, (v3f
){0,0,1}, VG_TAUf
* isst
* 0.5f
);
650 q_mul( kf_backpack
.q
, qspin
, kf_backpack
.q
);
651 kf_backpack
.co
[1] += isst
* 1.0f
;
652 v3_muladds( kf_backpack
.co
,
653 localplayer
.final_mtx
[ localplayer
.id_chest
][0],
658 scale
= vg_lerpf( 0.2f
, 1.0f
, vg_smoothstepf(player_glide
.t
) );
662 v3_fill( kf_backpack
.s
, scale
);
664 v3_copy( pose
->root_co
, kf_res
.co
);
665 v4_copy( pose
->root_q
, kf_res
.q
);
666 v3_fill( kf_res
.s
, scale
);
668 f32 blend
= vg_smoothstepf( vg_maxf( 0, player_glide
.t
) );
669 keyframe_lerp( &kf_backpack
, &kf_res
, blend
, &kf_res
);
673 q_m3x3( kf_res
.q
, mmdl
);
674 m3x3_scale( mmdl
, kf_res
.s
);
675 v3_copy( kf_res
.co
, mmdl
[3] );
677 render_glider_model( cam
, world
, mmdl
, k_board_shader_player
);
680 v4_copy( kf_res
.q
, player_glide
.remote_animator
.root_q
);
681 v3_copy( kf_res
.co
, player_glide
.remote_animator
.root_co
);
682 player_glide
.remote_animator
.s
= kf_res
.s
[0];
685 void player_glide_render_effects( vg_camera
*cam
)
689 rb_extrapolate( &player_glide
.rb
, co
, q
);
690 q_mulv( q
, (v3f
){0,-0.5f
,0}, temp
);
691 v3_add( temp
, co
, co
);
693 f32 alpha
= vg_maxf( (fabsf(player_glide
.info_lift
[2])-1.0f
), 0.0f
) /18.0f
;
695 for( u32 i
=0; i
<player_glide
.trail_count
; i
++ ){
697 q_mulv( q
, player_glide
.trail_positions
[i
], vvert
);
698 v3_add( co
, vvert
, vvert
);
700 trail_system_update( &trails_glider
[i
], vg
.time_delta
, vvert
,
701 localplayer
.rb
.to_world
[1], alpha
);
703 trail_system_prerender( &trails_glider
[i
] );
704 trail_system_render( &trails_glider
[i
], &skaterift
.cam
);