the never ending refactor
[carveJwlIkooP6JGAAIwe30JlM.git] / player_walk.c
1 #ifndef PLAYER_WALK_C
2 #define PLAYER_WALK_C
3
4 #include "player.h"
5
6 VG_STATIC void player__walk_pre_update( player_instance *player )
7 {
8 struct player_walk *w = &player->_walk;
9 player_look( player, w->state.angles );
10
11 if( vg_input_button_down( player->input_use ) )
12 {
13 v3f xy_speed, v;
14 v3_copy( player->rb.v, v );
15 v3_copy( v, xy_speed );
16 xy_speed[1] = 0.0f;
17
18 if( v3_length2( xy_speed ) < 0.1f * 0.1f )
19 {
20 v[0] = -sinf( -w->state.angles[0] );
21 v[1] = 0.0f;
22 v[2] = -cosf( -w->state.angles[0] );
23 v3_muls( v, 1.6f, v );
24 }
25
26 player->subsystem = k_player_subsystem_skate;
27 player__skate_transition( player, v );
28 return;
29 }
30 }
31
32 VG_STATIC int player_walk_normal_standable( v3f n )
33 {
34 return n[1] > 0.70710678118f;
35 }
36
37 VG_STATIC void player_accelerate( v3f v, v3f movedir, float speed, float accel )
38 {
39 float currentspeed = v3_dot( v, movedir ),
40 addspeed = speed - currentspeed;
41
42 if( addspeed <= 0 )
43 return;
44
45 float accelspeed = accel * k_rb_delta * speed;
46
47 if( accelspeed > addspeed )
48 accelspeed = addspeed;
49
50 v3_muladds( v, movedir, accelspeed, v );
51 }
52
53 VG_STATIC void player_friction( v3f v )
54 {
55 float speed = v3_length( v ),
56 drop = 0.0f,
57 control = vg_maxf( speed, k_stopspeed );
58
59 if( speed < 0.04f )
60 return;
61
62 drop += control * k_walk_friction * k_rb_delta;
63
64 float newspeed = vg_maxf( 0.0f, speed - drop );
65 newspeed /= speed;
66
67 v3_muls( v, newspeed, v );
68 }
69
70 VG_STATIC void player__walk_update( player_instance *player )
71 {
72 struct player_walk *w = &player->_walk;
73 v3_copy( player->rb.co, w->state.prev_pos );
74
75 w->collider.height = 2.0f;
76 w->collider.radius = 0.3f;
77
78 m4x3f mtx;
79 m3x3_identity( mtx );
80 v3_add( player->rb.co, (v3f){0.0f, 1.0f, 0.0f}, mtx[3] );
81
82 debug_capsule( mtx, w->collider.radius, w->collider.height, VG__WHITE );
83
84 rb_ct manifold[64];
85 int len;
86
87 float yaw = w->state.angles[0];
88
89 v3f forward_dir = { sinf(yaw), 0.0f, -cosf(yaw) };
90 v3f right_dir = { -forward_dir[2], 0.0f, forward_dir[0] };
91
92 v2f walk = { player->input_walkh->axis.value,
93 player->input_walkv->axis.value };
94
95 if( v2_length2(walk) > 0.001f )
96 v2_normalize_clamp( walk );
97
98 w->move_speed = v2_length( walk );
99
100 /*
101 * Collision detection
102 */
103 len = rb_capsule__scene( mtx, &w->collider, NULL,
104 &world.rb_geo.inf.scene, manifold );
105 rb_manifold_filter_coplanar( manifold, len, 0.01f );
106 len = rb_manifold_apply_filtered( manifold, len );
107
108 v3f surface_avg = { 0.0f, 0.0f, 0.0f };
109 w->state.activity = k_walk_activity_air;
110
111 for( int i=0; i<len; i++ )
112 {
113 struct contact *ct = &manifold[i];
114 rb_debug_contact( ct );
115
116 if( player_walk_normal_standable( ct->n ) )
117 {
118 w->state.activity = k_walk_activity_ground;
119 v3_add( surface_avg, ct->n, surface_avg );
120 }
121
122 rb_prepare_contact( ct );
123 }
124
125
126 /*
127 * Move & Friction
128 */
129 float accel_speed = 0.0f, nominal_speed = 0.0f;
130 v3f movedir;
131 v3_muls( right_dir, walk[0], movedir );
132 v3_muladds( movedir, forward_dir, walk[1], movedir );
133
134 if( w->state.activity == k_walk_activity_ground )
135 {
136 v3_normalize( surface_avg );
137
138 v3f tx, ty;
139 rb_tangent_basis( surface_avg, tx, ty );
140
141 if( v2_length2(walk) > 0.001f )
142 {
143 /* clip movement to the surface */
144 float d = v3_dot(surface_avg,movedir);
145 v3_muladds( movedir, surface_avg, -d, movedir );
146 }
147
148 accel_speed = k_walk_accel;
149 nominal_speed = k_walkspeed;
150
151 /* jump */
152 if( player->input_jump->button.value )
153 {
154 player->rb.v[1] = 5.0f;
155 w->state.activity = k_walk_activity_air;
156 accel_speed = k_walk_air_accel;
157 nominal_speed = k_airspeed;
158 }
159 else
160 {
161 player_friction( player->rb.v );
162
163 struct world_material *surface_mat = world_contact_material(manifold);
164 w->surface = surface_mat->info.surface_prop;
165 }
166 }
167 else
168 {
169 accel_speed = k_walk_air_accel;
170 nominal_speed = k_airspeed;
171 }
172
173 if( v2_length2(walk) > 0.001f )
174 {
175 player_accelerate( player->rb.v, movedir, nominal_speed, accel_speed );
176 v3_normalize( movedir );
177 }
178
179 /*
180 * Resolve velocity constraints
181 */
182 for( int j=0; j<5; j++ )
183 {
184 for( int i=0; i<len; i++ )
185 {
186 struct contact *ct = &manifold[i];
187
188 /*normal */
189 float vn = -v3_dot( player->rb.v, ct->n );
190
191 float temp = ct->norm_impulse;
192 ct->norm_impulse = vg_maxf( temp + vn, 0.0f );
193 vn = ct->norm_impulse - temp;
194
195 v3_muladds( player->rb.v, ct->n, vn, player->rb.v );
196 }
197 }
198
199 /*
200 * Depenetrate
201 */
202 v3f dt;
203 v3_zero( dt );
204 for( int j=0; j<8; j++ )
205 {
206 for( int i=0; i<len; i++ )
207 {
208 struct contact *ct = &manifold[i];
209
210 float resolved_amt = v3_dot( ct->n, dt ),
211 remaining = (ct->p-k_penetration_slop) - resolved_amt,
212 apply = vg_maxf( remaining, 0.0f ) * 0.3f;
213
214 v3_muladds( dt, ct->n, apply, dt );
215 }
216 }
217 v3_add( dt, player->rb.co, player->rb.co );
218
219 /* TODO: Stepping......
220 *
221 * ideas; walkgrid style steps
222 */
223 #if 0
224 if( w->state.activity == k_walk_activity_ground )
225 {
226 /* step */
227 float max_dist = 0.4f;
228
229 v3f pa, pb;
230 v3_copy( player->rb.co, pa );
231 pa[1] += w->collider.radius + max_dist;
232
233 v3_muladds( pa, (v3f){0.0f,1.0f,0.0f}, -max_dist * 2.0f, pb );
234 vg_line( pa, pb, 0xff000000 );
235
236 v3f n;
237 float t;
238 if( spherecast_world( pa, pb, w->collider.radius, &t, n ) != -1 )
239 {
240 if( player_walk_normal_standable( n ) )
241 {
242 v3_lerp( pa, pb, t, player->rb.co );
243 player->rb.co[1] -= w->collider.radius;
244 }
245 }
246 }
247 #endif
248
249
250 /* integrate */
251 if( w->state.activity == k_walk_activity_air )
252 player->rb.v[1] += -k_gravity * k_rb_delta;
253
254 v3_muladds( player->rb.co, player->rb.v, k_rb_delta, player->rb.co );
255
256
257 v3_add( player->rb.co, (v3f){0.0f, 1.0f, 0.0f}, mtx[3] );
258 debug_capsule( mtx, w->collider.radius, w->collider.height, VG__GREEN );
259
260 /*
261 * CCD routine
262 * ---------------------------------------------------
263 *
264 */
265 v3f lwr_prev,
266 lwr_now,
267 lwr_offs = { 0.0f, w->collider.radius, 0.0f };
268
269 v3_add( lwr_offs, w->state.prev_pos, lwr_prev );
270 v3_add( lwr_offs, player->rb.co, lwr_now );
271
272 v3f movedelta;
273 v3_sub( player->rb.co, w->state.prev_pos, movedelta );
274
275 float movedist = v3_length( movedelta );
276
277 if( movedist > 0.3f )
278 {
279 float t, sr = w->collider.radius-0.04f;
280 v3f n;
281
282 if( spherecast_world( lwr_prev, lwr_now, sr, &t, n ) != -1 )
283 {
284 v3_lerp( lwr_prev, lwr_now, vg_maxf(0.01f,t), player->rb.co );
285 player->rb.co[1] -= w->collider.radius;
286 rb_update_transform( &player->rb );
287
288 v3_add( player->rb.co, (v3f){0.0f, 1.0f, 0.0f}, mtx[3] );
289 debug_capsule( mtx, w->collider.radius, w->collider.height, VG__RED );
290 }
291 }
292
293 teleport_gate *gate;
294 if( (gate = world_intersect_gates( player->rb.co, w->state.prev_pos )) )
295 {
296 m4x3_mulv( gate->transport, player->rb.co, player->rb.co );
297 m3x3_mulv( gate->transport, player->rb.v, player->rb.v );
298 rb_update_transform( &player->rb );
299
300 /* analytical rotation of yaw */
301 v3f fwd_dir = { cosf(w->state.angles[0]),
302 0.0f,
303 sinf(w->state.angles[0])};
304 m3x3_mulv( gate->transport, fwd_dir, fwd_dir );
305 w->state.angles[0] = atan2f( fwd_dir[2], fwd_dir[0] );
306
307 w->state_gate_storage = w->state;
308 player__pass_gate( player, gate );
309 }
310 }
311
312 VG_STATIC void player__walk_post_update( player_instance *player )
313 {
314 struct player_walk *w = &player->_walk;
315
316 m4x3f mtx;
317 m3x3_identity( mtx );
318 v3_add( player->rb.co, (v3f){0.0f, 1.0f, 0.0f}, mtx[3] );
319
320 float substep = vg_clampf( vg.accumulator / k_rb_delta, 0.0f, 1.0f );
321 v3_muladds( mtx[3], player->rb.v, k_rb_delta*substep, mtx[3] );
322 debug_capsule( mtx, w->collider.radius, w->collider.height, VG__YELOW );
323 }
324
325 VG_STATIC void player__walk_animate( player_instance *player,
326 player_animation *dest )
327 {
328 struct player_walk *w = &player->_walk;
329 struct skeleton *sk = &player->playeravatar->sk;
330
331 {
332 float fly = (w->state.activity == k_walk_activity_air)? 1.0f: 0.0f,
333 rate;
334
335 if( w->state.activity == k_walk_activity_air )
336 rate = 2.4f;
337 else
338 rate = 9.0f;
339
340 w->blend_fly = vg_lerpf( w->blend_fly, fly, rate*vg.time_delta );
341 w->blend_run = vg_lerpf( w->blend_run,
342 w->move_speed *
343 (1.0f + player->input_walk->button.value*0.5f),
344 2.0f*vg.time_delta );
345 }
346
347 player_pose apose, bpose;
348
349 if( w->move_speed > 0.025f )
350 {
351 /* TODO move */
352 float walk_norm = 30.0f/(float)w->anim_walk->length,
353 run_norm = 30.0f/(float)w->anim_run->length,
354 walk_adv = vg_lerpf( walk_norm, run_norm, w->move_speed );
355
356 w->walk_timer += walk_adv * vg.time_delta;
357 }
358 else
359 {
360 w->walk_timer = 0.0f;
361 }
362
363 float walk_norm = (float)w->anim_walk->length/30.0f,
364 run_norm = (float)w->anim_run->length/30.0f,
365 t = w->walk_timer,
366 l = vg_clampf( w->blend_run*15.0f, 0.0f, 1.0f ),
367 idle_walk = vg_clampf( (w->blend_run-0.1f)/(1.0f-0.1f), 0.0f, 1.0f );
368
369 /* walk/run */
370 skeleton_sample_anim( sk, w->anim_walk, t*walk_norm, apose );
371 skeleton_sample_anim( sk, w->anim_run, t*run_norm, bpose );
372
373 skeleton_lerp_pose( sk, apose, bpose, l, apose );
374
375 /* idle */
376 skeleton_sample_anim( sk, w->anim_idle, vg.time*0.1f, bpose );
377 skeleton_lerp_pose( sk, apose, bpose, 1.0f-idle_walk, apose );
378
379 /* air */
380 skeleton_sample_anim( sk, w->anim_jump, vg.time*0.6f, bpose );
381 skeleton_lerp_pose( sk, apose, bpose, w->blend_fly, dest->pose );
382
383 /* Create transform */
384 rb_extrapolate( &player->rb, dest->root_co, dest->root_q );
385 q_axis_angle( dest->root_q, (v3f){0.0f,1.0f,0.0f},
386 -w->state.angles[0]-VG_PIf*0.5f );
387 }
388
389 VG_STATIC void player__walk_post_animate( player_instance *player )
390 {
391 /*
392 * Camera
393 */
394 struct player_walk *w = &player->_walk;
395 struct player_avatar *av = player->playeravatar;
396
397 /* 3RD */
398 m3x3f angles;
399 euler_m3x3( w->state.angles, angles );
400
401 v3f cast_dir, origin;
402
403 v3_add( player->rb.co, (v3f){0.0f,2.0f,0.0f}, origin );
404
405 v3_muladds( origin, angles[2], 2.0f, player->cam3.co );
406 v3_muladds( player->cam3.co, angles[0], 0.5f, player->cam3.co );
407
408 float t;
409 v3f n;
410 if( spherecast_world( origin, player->cam3.co, 0.1f, &t, n ) != -1 )
411 v3_lerp( origin, player->cam3.co, t, player->cam3.co );
412 v3_copy( w->state.angles, player->cam3.angles );
413
414 /* 1ST */
415 /* FIXME: viewpoint entity */
416 v3f vp = {-0.1f,1.8f,0.0f};
417 m4x3_mulv( av->sk.final_mtx[ av->id_head-1 ], vp, player->cam1.co );
418 v3_copy( w->state.angles, player->cam1.angles );
419
420 /* FIXME: Organize this. Its int wrong fucking place */
421 v3f vp0 = {0.0f,0.1f, 0.6f},
422 vp1 = {0.0f,0.1f,-0.6f};
423
424 m4x3_mulv( av->sk.final_mtx[ av->id_board ], vp0, TEMP_BOARD_0 );
425 m4x3_mulv( av->sk.final_mtx[ av->id_board ], vp1, TEMP_BOARD_1 );
426 }
427
428
429 VG_STATIC void player__walk_im_gui( player_instance *player )
430 {
431 player__debugtext( 1, "V: %5.2f %5.2f %5.2f",player->rb.v[0],
432 player->rb.v[1],
433 player->rb.v[2] );
434 player__debugtext( 1, "CO: %5.2f %5.2f %5.2f",player->rb.co[0],
435 player->rb.co[1],
436 player->rb.co[2] );
437 }
438
439 VG_STATIC void player__walk_bind( player_instance *player )
440 {
441 struct player_walk *w = &player->_walk;
442 struct player_avatar *av = player->playeravatar;
443 struct skeleton *sk = &av->sk;
444
445 w->anim_idle = skeleton_get_anim( sk, "idle_cycle" );
446 w->anim_walk = skeleton_get_anim( sk, "walk" );
447 w->anim_run = skeleton_get_anim( sk, "run" );
448 w->anim_jump = skeleton_get_anim( sk, "jump" );
449 }
450
451 VG_STATIC void player__walk_transition( player_instance *player, v3f angles )
452 {
453 struct player_walk *w = &player->_walk;
454 v3_copy( angles, w->state.angles );
455 }
456
457 #endif /* PLAYER_DEVICE_WALK_H */