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