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