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