('1','wood',''),
('2','grass',''),
('3','tiles',''),
- ('4','metal','')
+ ('4','metal',''),
+ ('5','snow (low friction)',''),
+ ('6','sand (medium friction)','')
])
collision: bpy.props.BoolProperty( \
--- /dev/null
+#include "world.h"
+#include "particle.h"
+
+static f32 k_tornado_strength = 0.0f,
+ k_tornado_ratio = 0.5f,
+ k_tornado_range = 10.f;
+
+static void ent_tornado_init(void){
+ vg_console_reg_var( "k_tonado_strength", &k_tornado_strength,
+ k_var_dtype_f32, VG_VAR_PERSISTENT|VG_VAR_CHEAT );
+ vg_console_reg_var( "k_tonado_ratio", &k_tornado_ratio,
+ k_var_dtype_f32, VG_VAR_PERSISTENT|VG_VAR_CHEAT );
+ vg_console_reg_var( "k_tonado_range", &k_tornado_range,
+ k_var_dtype_f32, VG_VAR_PERSISTENT|VG_VAR_CHEAT );
+}
+
+static void ent_tornado_debug(void) {
+ world_instance *world = world_current_instance();
+ for( u32 i=0; i<mdl_arrcount(&world->ent_marker); i ++ ){
+ ent_marker *marker = mdl_arritm( &world->ent_marker, i );
+
+ if( MDL_CONST_PSTREQ( &world->meta, marker->pstr_alias, "tornado" ) ){
+ v3f p1;
+ v3_add( marker->transform.co, (v3f){0,20,0}, p1 );
+ vg_line( marker->transform.co, p1, VG__RED );
+
+ m4x3f mmdl;
+ m4x3_identity( mmdl );
+ v3_copy( marker->transform.co, mmdl[3] );
+ vg_line_sphere( mmdl, k_tornado_range, 0 );
+ }
+ }
+}
+
+static void ent_tornado_forces( v3f co, v3f cv, v3f out_a ){
+ world_instance *world = world_current_instance();
+ v3_zero( out_a );
+
+ for( u32 i=0; i<mdl_arrcount(&world->ent_marker); i ++ ){
+ ent_marker *marker = mdl_arritm( &world->ent_marker, i );
+
+ if( MDL_CONST_PSTREQ( &world->meta, marker->pstr_alias, "tornado" ) ){
+ v3f d, dir;
+ v3_sub( co, marker->transform.co, d );
+ d[1] = 0.0f;
+
+ f32 dist = v3_length( d );
+ v3_normalize( d );
+
+ v3_cross( d, (v3f){0,1,0}, dir );
+ if( v3_dot( dir, cv ) < 0.0f )
+ v3_negate( dir, dir );
+
+ f32 s = vg_maxf(0.0f, 1.0f-dist/k_tornado_range),
+ F0 = s*k_tornado_strength,
+ F1 = s*s*k_tornado_strength;
+
+ v3_muladds( out_a, dir, F0 * k_tornado_ratio, out_a );
+ v3_muladds( out_a, d, F1 * -(1.0f-k_tornado_ratio), out_a );
+ }
+ }
+}
+
+static void ent_tornado_pre_update(void){
+ world_instance *world = world_current_instance();
+ for( u32 i=0; i<mdl_arrcount(&world->ent_marker); i ++ ){
+ ent_marker *marker = mdl_arritm( &world->ent_marker, i );
+
+ if( MDL_CONST_PSTREQ( &world->meta, marker->pstr_alias, "tornado" ) ){
+ v3f co;
+ vg_rand_sphere( &vg.rand, co );
+
+ v3f tangent = { co[2], 0, co[0] };
+
+ f32 s = vg_signf( co[1] );
+ v3_muls( tangent, s*10.0f, tangent );
+ co[1] *= s;
+
+ v3_muladds( marker->transform.co, co, k_tornado_range, co );
+ particle_spawn( &particles_env, co, tangent, 2.0f, 0xffffffff );
+ }
+ }
+}
};
enum mdl_surface_prop{
- k_surface_prop_concrete = 0,
- k_surface_prop_wood = 1,
- k_surface_prop_grass = 2,
- k_surface_prop_tiles = 3,
- k_surface_prop_metal = 4
+ k_surface_prop_concrete = 0,
+ k_surface_prop_wood = 1,
+ k_surface_prop_grass = 2,
+ k_surface_prop_tiles = 3,
+ k_surface_prop_metal = 4,
+ k_surface_prop_snow = 5,
+ k_surface_prop_sand = 6
};
enum material_flag{
.scale = 0.02f,
.velocity_scale = 0.001f,
.width = 0.0125f
+},
+particles_env = {
+ .scale = 0.04f,
+ .velocity_scale = 0.001f,
+ .width = 0.25f
};
static void particle_alloc( particle_system *sys, u32 max );
#include "player_skate.c"
#include "player_dead.c"
#include "player_drive.c"
+#include "player_glide.c"
#include "player_basic_info.c"
#include "player_render.c"
k_player_subsystem_dead = 2,
k_player_subsystem_drive = 3,
k_player_subsystem_basic_info = 4,
+ k_player_subsystem_glide = 5,
k_player_subsystem_max,
k_player_subsystem_invalid = 255
};
#include "player_skate.h"
#include "player_dead.h"
#include "player_drive.h"
+#include "player_glide.h"
#include "player_basic_info.h"
#include "player_replay.h"
[k_player_subsystem_dead] = &player_subsystem_dead,
[k_player_subsystem_drive] = &player_subsystem_drive,
[k_player_subsystem_skate] = &player_subsystem_skate,
- [k_player_subsystem_basic_info]=&player_subsystem_basic_info
+ [k_player_subsystem_basic_info]=&player_subsystem_basic_info,
+ [k_player_subsystem_glide] = &player_subsystem_glide,
};
/*
if( !ef->colour ) return;
if( ef->t < 0.0f ){
- ef->t = 0.05f+vg_randf64(&vg.rand)*0.1f;
+ ef->t += 0.05f+vg_randf64(&vg.rand)*0.1f;
v3f dir;
v3_copy( v, dir );
struct player_effects_data {
effect_blink blink;
- effect_spark spark;
+ effect_spark spark, sand;
};
#endif /* PLAYER_EFFECTS */
--- /dev/null
+#ifndef PLAYER_GLIDE_C
+#define PLAYER_GLIDE_C
+
+#include "player_glide.h"
+#include "input.h"
+
+static f32 k_glide_steer = 2.0f,
+ k_glide_cl = 0.04f,
+ k_glide_cs = 0.02f,
+ k_glide_drag = 0.0001f,
+ k_glide_slip_yaw = 0.1f,
+ k_glide_lift_pitch = 0.0f,
+ k_glide_wing_orient = -0.1f,
+ k_glide_balance = 1.0f;
+
+static i32 k_glide_pause = 0;
+
+static void player_glide_pre_update(void){
+}
+
+static void massless_accel( rigidbody *rb, v3f delta, v3f impulse ){
+ /* linear */
+ v3_muladds( rb->v, impulse, k_rb_delta, rb->v );
+
+ /* Angular velocity */
+ v3f wa;
+ v3_cross( delta, impulse, wa );
+ v3_muladds( rb->w, wa, k_rb_delta, rb->w );
+}
+
+static void calculate_lift( v3f vl, f32 aoa_bias,
+ v3f axis, v3f back, f32 power,
+ v3f out_force ){
+ v3f up;
+ v3_cross( back, axis, up );
+
+ v3f wind;
+ v3_muladds( vl, axis, -v3_dot(axis,vl), wind );
+
+ f32 windv2 = v3_length2(wind),
+ aoa = atan2f( v3_dot( up, wind ), v3_dot( back, wind ) ) + aoa_bias,
+ cl = aoa / VG_PIf, /* TODO: make it a curve */
+ L = windv2 * cl * power;
+
+ v3f lift_dir;
+ v3_normalize( wind );
+ v3_cross( wind, axis, lift_dir );
+
+ /* this is where induced drag (from the flappy things) would go */
+
+ v3_muls( lift_dir, L, out_force );
+}
+
+static void calculate_drag( v3f vl, f32 cd, v3f out_force ){
+ f32 v2 = v3_length2( vl );
+ v3f dir;
+ v3_copy( vl, dir );
+ v3_normalize( dir );
+ v3_muls( vl, -cd*v2, out_force );
+}
+
+static void player_glide_update(void){
+ rigidbody *rb = &localplayer.rb;
+ vg_line_sphere( rb->to_world, 1.0f, 0 );
+
+ v2f steer;
+ joystick_state( k_srjoystick_steer, steer );
+
+ /* lift */
+ v3f vl, wl;
+ m3x3_mulv( rb->to_local, rb->v, vl );
+ m3x3_mulv( rb->to_local, rb->w, wl );
+
+ v3f F, Flift, Fslip, Fdrag, FslipW, FliftW;
+
+ calculate_lift( vl, steer[1]*k_glide_steer,
+ (v3f){1,0,0},
+ (v3f){0,sinf(k_glide_wing_orient),cosf(k_glide_wing_orient)},
+ k_glide_cl, Flift );
+ v3_copy( Flift, player_glide.info_lift );
+ v3_cross( (v3f){0,0,0}, Flift, FliftW );
+
+ calculate_lift( vl, 0.0f,
+ (v3f){0,1,0},(v3f){0,0,1},
+ k_glide_cs, Fslip );
+ v3_copy( Fslip, player_glide.info_slip );
+ v3_cross( (v3f){0,k_glide_lift_pitch,k_glide_slip_yaw}, Fslip, FslipW );
+
+ calculate_drag( vl, k_glide_drag, Fdrag );
+ v3_copy( Fdrag, player_glide.info_drag );
+
+ v3f balance = {0.0f,-k_glide_balance,0.0f};
+ m3x3_mulv( rb->to_local, balance, balance );
+ vg_info( PRINTF_v3f( balance ) );
+
+ v3f Fw = {
+ steer[1]*k_glide_steer - balance[2],
+ 0.0f,
+ -steer[0]*k_glide_steer + balance[0],
+ };
+
+ if( player_glide.ticker ){
+ player_glide.ticker --;
+ return;
+ }
+ player_glide.ticker += k_glide_pause;
+
+ /* apply forces */
+ v3_add( Flift, Fslip, F );
+ v3_add( F, Fdrag, F );
+
+ m3x3_mulv( rb->to_world, F, F );
+ v3_muladds( rb->v, F, k_rb_delta, rb->v );
+
+ v3_add( Fw, FslipW, Fw );
+ v3_add( Fw, FliftW, Fw );
+ m3x3_mulv( rb->to_world, Fw, Fw );
+ v3_muladds( rb->w, Fw, k_rb_delta, rb->w );
+
+ rb_iter( rb );
+ rb_update_transform( rb );
+}
+
+static void player_glide_post_update(void){
+}
+
+static void player_glide_animate(void){
+ struct player_glide *g = &player_glide;
+ struct player_glide_animator *animator = &g->animator;
+ rb_extrapolate( &localplayer.rb, animator->root_co, animator->root_q );
+}
+
+static void player_glide_pose( void *_animator, player_pose *pose ){
+ struct skeleton *sk = &localplayer.skeleton;
+ struct player_glide_animator *animator = _animator;
+
+ skeleton_sample_anim( sk, player_glide.anim_temp, 0.0f, pose->keyframes );
+
+ v3_copy( animator->root_co, pose->root_co );
+ v4_copy( animator->root_q, pose->root_q );
+}
+
+static void player_glide_post_animate(void){
+ if( localplayer.cam_control.camera_mode == k_cam_firstperson )
+ localplayer.cam_velocity_influence = 0.0f;
+ else
+ localplayer.cam_velocity_influence = 0.0f;
+
+ v3f fwd;
+ v3_muls( localplayer.rb.to_world[2], -1.0f, fwd );
+ v3_angles( fwd, localplayer.angles );
+}
+
+static void player_glide_im_gui(void){
+ player__debugtext( 1, "Nothing here" );
+ player__debugtext( 1, " lift: %.2f %.2f %.2f",
+ player_glide.info_lift[0],
+ player_glide.info_lift[1],
+ player_glide.info_lift[2] );
+ player__debugtext( 1, " slip: %.2f %.2f %.2f",
+ player_glide.info_slip[0],
+ player_glide.info_slip[1],
+ player_glide.info_slip[2] );
+ player__debugtext( 1, " drag: %.2f %.2f %.2f",
+ player_glide.info_drag[0],
+ player_glide.info_drag[1],
+ player_glide.info_drag[2] );
+}
+
+static void player_glide_bind(void){
+ struct skeleton *sk = &localplayer.skeleton;
+ player_glide.anim_temp = skeleton_get_anim( sk, "idle_cycle+y" );
+
+ u32 mask = VG_VAR_CHEAT|VG_VAR_PERSISTENT;
+ VG_VAR_F32( k_glide_steer, flags=mask );
+ VG_VAR_F32( k_glide_cl, flags=mask );
+ VG_VAR_F32( k_glide_cs, flags=mask );
+ VG_VAR_F32( k_glide_drag, flags=mask );
+ VG_VAR_F32( k_glide_slip_yaw, flags=mask );
+ VG_VAR_F32( k_glide_lift_pitch, flags=mask );
+ VG_VAR_I32( k_glide_pause, flags=mask );
+ VG_VAR_F32( k_glide_balance, flags=mask );
+ VG_VAR_F32( k_glide_wing_orient, flags=mask );
+}
+
+#endif /* PLAYER_GLIDE_C */
--- /dev/null
+#ifndef PLAYER_GLIDE_H
+#define PLAYER_GLIDE_H
+
+#include "player.h"
+
+struct player_glide {
+ struct skeleton_anim *anim_temp;
+
+ struct player_glide_animator {
+ v3f root_co;
+ v4f root_q;
+ }
+ animator;
+
+ v3f info_lift,
+ info_slip,
+ info_drag;
+
+ u32 ticker;
+}
+static player_glide;
+
+static void player_glide_pre_update(void);
+static void player_glide_update(void);
+static void player_glide_post_update(void);
+static void player_glide_animate(void);
+static void player_glide_pose( void *animator, player_pose *pose );
+
+static void player_glide_post_animate(void);
+static void player_glide_im_gui(void);
+static void player_glide_bind(void);
+
+struct player_subsystem_interface static player_subsystem_glide = {
+ .pre_update = player_glide_pre_update,
+ .update = player_glide_update,
+ .post_update = player_glide_post_update,
+ .animate = player_glide_animate,
+ .pose = player_glide_pose,
+ .post_animate = player_glide_post_animate,
+ .im_gui = player_glide_im_gui,
+ .bind = player_glide_bind,
+
+ .animator_data = &player_glide.animator,
+ .animator_size = sizeof(player_glide.animator),
+ .name = "Glide"
+};
+
+#endif /* PLAYER_GLIDE_H */
u16 gamestate_size = 0;
if( save_state ){
+ /* TODO: have as part of system struct */
gamestate_size = (u32 []){
[k_player_subsystem_walk ] = sizeof(struct player_walk_state),
[k_player_subsystem_drive] = 0,
[k_player_subsystem_skate] = sizeof(struct player_skate_state),
[k_player_subsystem_dead ] = localplayer.ragdoll.part_count *
- sizeof(struct replay_rb)
+ sizeof(struct replay_rb),
+ [k_player_subsystem_glide] = 0,
}[ localplayer.subsystem ];
}
- u16 animator_size = (u16 []){
- [k_player_subsystem_walk ] = sizeof(struct player_walk_animator),
- [k_player_subsystem_drive] = 0,
- [k_player_subsystem_skate] = sizeof(struct player_skate_animator),
- [k_player_subsystem_dead ] = sizeof(struct player_dead_animator)
- }[ localplayer.subsystem ];
+ u16 animator_size = player_subsystems[localplayer.subsystem]->animator_size;
replay_frame *frame = replay_newframe( replay,
animator_size, gamestate_size,
else if( localplayer.subsystem == k_player_subsystem_dead ){
memcpy( dst, &player_dead.animator, animator_size );
}
+ else if( localplayer.subsystem == k_player_subsystem_glide ){
+ memcpy( dst, &player_glide.animator, animator_size );
+ }
/* sound effects */
memcpy( replay_frame_data( frame, k_replay_framedata_sfx ),
#include "ent_skateshop.h"
#include "addon.h"
+#include "ent_tornado.c"
+
static void player__skate_bind(void){
struct skeleton *sk = &localplayer.skeleton;
rb_update_transform( &localplayer.rb );
inf->gravity = gravity;
v3_copy( launch_v, inf->v );
+ /* initial conditions */
+ v3f v;
+ v3_copy( launch_v, v );
+ v3_copy( launch_co, co1 );
+
for( int i=1; i<=50; i++ ){
- float t = (float)i * k_trace_delta;
+ f32 t = (f32)i * k_trace_delta;
- v3_muls( launch_v, t, co1 );
- co1[1] += -0.5f * gravity * t*t;
- v3_add( launch_co, co1, co1 );
+ /* integrate forces */
+ v3f a;
+ ent_tornado_forces( co1, v, a );
+ a[1] -= gravity;
- float launch_vy = launch_v[1];
+ /* position */
+ v3_muladds( co1, v, k_trace_delta, co1 );
+ v3_muladds( co1, a, 0.5f*k_trace_delta*k_trace_delta, co1 );
+
+ /* velocity */
+ v3_muladds( v, a, k_trace_delta, v );
int search_for_grind = 1;
if( grind_located ) search_for_grind = 0;
- if( launch_vy - gravity*t > 0.0f ) search_for_grind = 0;
+ if( v[1] > 0.0f ) search_for_grind = 0;
/* REFACTOR */
}
if( search_for_grind ){
- v3f ve;
- v3_copy( launch_v, ve );
- ve[1] += -gravity * t;
-
- if( skate_grind_scansq( closest, ve, 0.5f, &grind ) ){
+ if( skate_grind_scansq( closest, v, 0.5f, &grind ) ){
/* check alignment */
- v2f v0 = { ve[0], ve[2] },
+ v2f v0 = { v[0], v[2] },
v1 = { grind.dir[0], grind.dir[2] };
v2_normalize( v0 );
a_min = cosf( VG_PIf * 0.05f );
/* check speed */
- if( (fabsf(v3_dot( ve, grind.dir ))>=k_grind_axel_min_vel) &&
+ if( (fabsf(v3_dot( v, grind.dir ))>=k_grind_axel_min_vel) &&
(a >= a_min) &&
(fabsf(grind.dir[1]) < 0.70710678118654752f))
{
world_tri_index_surface( trace_world, tri[0] );
inf->type = k_prediction_land;
-
- v3f ve;
- v3_copy( launch_v, ve );
- ve[1] += -gravity * t;
-
- inf->score = -v3_dot( ve, inf->n );
+ inf->score = -v3_dot( v, inf->n );
inf->land_dist = t + k_trace_delta * t1;
/* Bias prediction towords ramps */
f32 lat = k_friction_lat;
if( fabsf(axis_state(k_sraxis_skid)) > 0.1f ){
- lat = k_friction_lat * 2.0f;
+ if( (player_skate.surface == k_surface_prop_snow) ||
+ (player_skate.surface == k_surface_prop_sand) ){
+ lat *= 8.0f;
+ }
+ else
+ lat *= 1.5f;
}
- vel[0] += vg_cfrictf( vel[0], k_friction_lat * k_rb_delta );
+ if( player_skate.surface == k_surface_prop_snow )
+ lat *= 0.5f;
+ else if( player_skate.surface == k_surface_prop_sand )
+ lat *= 0.6f;
+
+ vel[0] += vg_cfrictf( vel[0], lat * k_rb_delta );
vel[2] += vg_cfrictf( vel[2], k_friction_resistance * k_rb_delta );
/* Pushing additive force */
if( button_down(k_srbind_use) && (v3_length2(state->trick_vel) < 0.01f) ){
localplayer.subsystem = k_player_subsystem_walk;
+ if( state->activity <= k_skate_activity_air_to_grind ){
+ localplayer.subsystem = k_player_subsystem_glide;
+ player__begin_holdout( (v3f){0,0,0} );
+ return;
+ }
+
v3f angles;
v3_copy( localplayer.cam.angles, localplayer.angles );
localplayer.angles[2] = 0.0f;
player__begin_holdout( offset );
player__walk_transition( state->activity <= k_skate_activity_air_to_grind?
0: 1, state->trick_euler[0] );
+
return;
}
skate_apply_trick_model();
skate_apply_pump_model();
+ ent_tornado_debug();
+ v3f a;
+ ent_tornado_forces( localplayer.rb.co, localplayer.rb.v, a );
+ v3_muladds( localplayer.rb.v, a, k_rb_delta, localplayer.rb.v );
+
begin_collision:;
/*
static void player__skate_effects( void *_animator, m4x3f *final_mtx,
struct player_board *board,
struct player_effects_data *effect_data ){
-
struct skeleton *sk = &localplayer.skeleton;
struct player_skate_animator *animator = _animator;
- if( animator->grind > 0.5f ){
- v3f vp0, vp1, vpc;
- if( board ){
- v3_copy((v3f){0.0f,0.02f, board->truck_positions[0][2]}, vp1 );
- v3_copy((v3f){0.0f,0.02f, board->truck_positions[1][2]}, vp0 );
- }
- else{
- v3_zero( vp0 );
- v3_zero( vp1 );
- }
+ v3f vp0, vp1, vpc;
+ if( board ){
+ v3_copy((v3f){0.0f,0.02f, board->truck_positions[0][2]}, vp1 );
+ v3_copy((v3f){0.0f,0.02f, board->truck_positions[1][2]}, vp0 );
+ }
+ else{
+ v3_zero( vp0 );
+ v3_zero( vp1 );
+ }
- v3f *board_mtx = final_mtx[ localplayer.id_board ];
- m4x3_mulv( board_mtx, vp0, vp0 );
- m4x3_mulv( board_mtx, vp1, vp1 );
- v3_add( vp0, vp1, vpc );
- v3_muls( vpc, 0.5f, vpc );
+ v3f *board_mtx = final_mtx[ localplayer.id_board ];
+ m4x3_mulv( board_mtx, vp0, vp0 );
+ m4x3_mulv( board_mtx, vp1, vp1 );
+ v3_add( vp0, vp1, vpc );
+ v3_muls( vpc, 0.5f, vpc );
+ if( animator->surface == k_surface_prop_sand ){
+ if( (animator->slide>0.4f) && (v3_length2(animator->root_v)>4.0f*4.0f) ){
+ v3f v, co;
+ v3_muls( animator->root_v, 0.5f, v );
+ v3_lerp( vp0, vp1, vg_randf64(&vg.rand), co );
+
+ effect_data->sand.colour = 0xff8ec4e6;
+ effect_spark_apply( &effect_data->sand, co, v, vg.time_delta * 8.0 );
+ }
+ }
+
+ if( animator->grind > 0.5f ){
int back = 0, front = 0, mid = 0;
if( animator->activity == k_skate_activity_grind_5050 ){
"wood",
"grass",
"tiles",
- "metal" }
+ "metal",
+ "snow",
+ "sand" }
[w->surface] );
}
replay_clear( &skaterift.replay );
particle_alloc( &particles_grind, 300 );
+ particle_alloc( &particles_env, 200 );
player_load_animation_reference( "models/ch_none.mdl" );
player_model_load( &localplayer.fallback_model, "models/ch_none.mdl" );
vg_loader_step( addon_system_init, NULL );
vg_loader_step( workshop_init, NULL );
vg_loader_step( skateshop_init, NULL );
+ vg_loader_step( ent_tornado_init, NULL );
vg_loader_step( skaterift_load_player_content, NULL );
//particle_system_debug( &particles_grind );
particle_system_prerender( &particles_grind );
particle_system_render( &particles_grind, &skaterift.cam );
+
+ ent_tornado_pre_update();
+ particle_system_update( &particles_env, vg.time_delta );
+ particle_system_prerender( &particles_env );
+ particle_system_render( &particles_env, &skaterift.cam );
/*
* render transition