1 #ifndef PLAYER_DEVICE_WALK_H
2 #define PLAYER_DEVICE_WALK_H
4 #include "player_interface.h"
6 #include "player_model.h"
8 struct player_device_walk
19 k_walk_activity_ground
,
27 enum mdl_surface_prop surface
;
29 struct skeleton_anim
*anim_walk
, *anim_run
, *anim_idle
, *anim_jump
;
39 VG_STATIC
void player_walk_pre_update( player_interface
*player
,
40 player_attachment
*at
)
42 struct player_device_walk
*w
= at
->storage
;
43 player_look( player
, w
->state
.angles
);
46 v3f walk
= { player
->input_walkh
->axis
.value
,
48 -player
->input_walkv
->axis
.value
};
50 v3_muls( walk
, 10.0f
* vg
.time_delta
, walk
);
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
);
60 VG_STATIC
int player_walk_normal_standable( v3f n
)
62 return n
[1] > 0.70710678118f
;
65 VG_STATIC
void player_accelerate( v3f v
, v3f movedir
, float speed
, float accel
)
67 float currentspeed
= v3_dot( v
, movedir
),
68 addspeed
= speed
- currentspeed
;
73 float accelspeed
= accel
* k_rb_delta
* speed
;
75 if( accelspeed
> addspeed
)
76 accelspeed
= addspeed
;
78 v3_muladds( v
, movedir
, accelspeed
, v
);
81 VG_STATIC
void player_friction( v3f v
)
83 float speed
= v3_length( v
),
85 control
= vg_maxf( speed
, k_stopspeed
);
90 drop
+= control
* k_walk_friction
* k_rb_delta
;
92 float newspeed
= vg_maxf( 0.0f
, speed
- drop
);
95 v3_muls( v
, newspeed
, v
);
98 VG_STATIC
void player_walk_update( player_interface
*player
,
99 player_attachment
*at
)
101 struct player_device_walk
*w
= at
->storage
;
102 w
->collider
.height
= 2.0f
;
103 w
->collider
.radius
= 0.3f
;
106 m3x3_identity( mtx
);
107 v3_add( player
->rb
.co
, (v3f
){0.0f
, 1.0f
, 0.0f
}, mtx
[3] );
109 debug_capsule( mtx
, w
->collider
.radius
, w
->collider
.height
, VG__WHITE
);
114 float yaw
= w
->state
.angles
[0];
116 v3f forward_dir
= { sinf(yaw
), 0.0f
, -cosf(yaw
) };
117 v3f right_dir
= { -forward_dir
[2], 0.0f
, forward_dir
[0] };
119 v2f walk
= { player
->input_walkh
->axis
.value
,
120 player
->input_walkv
->axis
.value
};
122 if( v2_length2(walk
) > 0.001f
)
123 v2_normalize_clamp( walk
);
125 w
->move_speed
= v2_length( walk
);
128 * Collision detection
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
);
135 v3f surface_avg
= { 0.0f
, 0.0f
, 0.0f
};
136 w
->state
.activity
= k_walk_activity_air
;
138 for( int i
=0; i
<len
; i
++ )
140 struct contact
*ct
= &manifold
[i
];
141 rb_debug_contact( ct
);
143 if( player_walk_normal_standable( ct
->n
) )
145 w
->state
.activity
= k_walk_activity_ground
;
146 v3_add( surface_avg
, ct
->n
, surface_avg
);
149 rb_prepare_contact( ct
);
156 float accel_speed
= 0.0f
, nominal_speed
= 0.0f
;
158 v3_muls( right_dir
, walk
[0], movedir
);
159 v3_muladds( movedir
, forward_dir
, walk
[1], movedir
);
161 if( w
->state
.activity
== k_walk_activity_ground
)
163 v3_normalize( surface_avg
);
166 rb_tangent_basis( surface_avg
, tx
, ty
);
168 if( v2_length2(walk
) > 0.001f
)
170 /* clip movement to the surface */
171 float d
= v3_dot(surface_avg
,movedir
);
172 v3_muladds( movedir
, surface_avg
, -d
, movedir
);
175 accel_speed
= k_walk_accel
;
176 nominal_speed
= k_walkspeed
;
179 if( player
->input_jump
->button
.value
)
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
;
188 player_friction( player
->rb
.v
);
190 struct world_material
*surface_mat
= world_contact_material(manifold
);
191 w
->surface
= surface_mat
->info
.surface_prop
;
196 accel_speed
= k_walk_air_accel
;
197 nominal_speed
= k_airspeed
;
200 if( v2_length2(walk
) > 0.001f
)
202 player_accelerate( player
->rb
.v
, movedir
, nominal_speed
, accel_speed
);
203 v3_normalize( movedir
);
207 * Resolve velocity constraints
209 for( int j
=0; j
<5; j
++ )
211 for( int i
=0; i
<len
; i
++ )
213 struct contact
*ct
= &manifold
[i
];
216 float vn
= -v3_dot( player
->rb
.v
, ct
->n
);
218 float temp
= ct
->norm_impulse
;
219 ct
->norm_impulse
= vg_maxf( temp
+ vn
, 0.0f
);
220 vn
= ct
->norm_impulse
- temp
;
222 v3_muladds( player
->rb
.v
, ct
->n
, vn
, player
->rb
.v
);
231 for( int j
=0; j
<8; j
++ )
233 for( int i
=0; i
<len
; i
++ )
235 struct contact
*ct
= &manifold
[i
];
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
;
241 v3_muladds( dt
, ct
->n
, apply
, dt
);
244 v3_add( dt
, player
->rb
.co
, player
->rb
.co
);
246 /* TODO: Stepping......
248 * ideas; walkgrid style steps
251 if( w
->state
.activity
== k_walk_activity_ground
)
254 float max_dist
= 0.4f
;
257 v3_copy( player
->rb
.co
, pa
);
258 pa
[1] += w
->collider
.radius
+ max_dist
;
260 v3_muladds( pa
, (v3f
){0.0f
,1.0f
,0.0f
}, -max_dist
* 2.0f
, pb
);
261 vg_line( pa
, pb
, 0xff000000 );
265 if( spherecast_world( pa
, pb
, w
->collider
.radius
, &t
, n
) != -1 )
267 if( player_walk_normal_standable( n
) )
269 v3_lerp( pa
, pb
, t
, player
->rb
.co
);
270 player
->rb
.co
[1] -= w
->collider
.radius
;
278 if( w
->state
.activity
== k_walk_activity_air
)
279 player
->rb
.v
[1] += -k_gravity
* k_rb_delta
;
281 v3_muladds( player
->rb
.co
, player
->rb
.v
, k_rb_delta
, player
->rb
.co
);
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
);
288 VG_STATIC
void player_walk_post_update( player_interface
*player
,
289 player_attachment
*at
)
291 struct player_device_walk
*w
= at
->storage
;
294 m3x3_identity( mtx
);
295 v3_add( player
->rb
.co
, (v3f
){0.0f
, 1.0f
, 0.0f
}, mtx
[3] );
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 debug_capsule( mtx
, w
->collider
.radius
, w
->collider
.height
, VG__YELOW
);
304 lwr_offs
= { 0.0f
, w
->collider
.radius
, 0.0f
};
306 v3_add( lwr_offs
, player
->prev_position
, lwr_prev
);
307 v3_add( lwr_offs
, player
->rb
.co
, lwr_now
);
310 v3_sub( player
->rb
.co
, player
->prev_position
, movedelta
);
312 float movedist
= v3_length( movedelta
);
314 if( movedist
> 0.3f
)
316 float t
, sr
= w
->collider
.radius
-0.04f
;
319 if( spherecast_world( lwr_prev
, lwr_now
, sr
, &t
, n
) != -1 )
321 v3_lerp( lwr_prev
, lwr_now
, vg_maxf(0.01f
,t
), player
->rb
.co
);
322 player
->rb
.co
[1] -= w
->collider
.radius
;
323 rb_update_transform( &player
->rb
);
325 v3_add( player
->rb
.co
, (v3f
){0.0f
, 1.0f
, 0.0f
}, mtx
[3] );
326 debug_capsule( mtx
, w
->collider
.radius
, w
->collider
.height
, VG__RED
);
331 VG_STATIC
void player_walk_ui( player_interface
*player
,
332 player_attachment
*at
)
334 player_debugtext( 1, "V: %5.2f %5.2f %5.2f",player
->rb
.v
[0],
337 player_debugtext( 1, "CO: %5.2f %5.2f %5.2f",player
->rb
.co
[0],
342 VG_STATIC
void player_walk_bind( player_interface
*player
,
343 player_attachment
*at
)
345 struct player_device_walk
*w
= at
->storage
;
346 struct player_avatar
*av
= player
->playeravatar
;
347 struct skeleton
*sk
= &av
->sk
;
349 w
->anim_idle
= skeleton_get_anim( sk
, "idle_cycle" );
350 w
->anim_walk
= skeleton_get_anim( sk
, "walk" );
351 w
->anim_run
= skeleton_get_anim( sk
, "run" );
352 w
->anim_jump
= skeleton_get_anim( sk
, "jump" );
355 VG_STATIC
void player_walk_pose( player_interface
*player
,
356 player_attachment
*at
,
357 player_pose pose
, m4x3f transform
)
359 struct player_device_walk
*w
= at
->storage
;
360 struct skeleton
*sk
= &player
->playeravatar
->sk
;
363 float fly
= (w
->state
.activity
== k_walk_activity_air
)? 1.0f
: 0.0f
,
366 if( w
->state
.activity
== k_walk_activity_air
)
371 w
->blend_fly
= vg_lerpf( w
->blend_fly
, fly
, rate
*vg
.time_delta
);
372 w
->blend_run
= vg_lerpf( w
->blend_run
,
374 (1.0f
+ player
->input_walk
->button
.value
*0.5f
),
375 2.0f
*vg
.time_delta
);
378 player_pose apose
, bpose
;
380 if( w
->move_speed
> 0.025f
)
383 float walk_norm
= 30.0f
/(float)w
->anim_walk
->length
,
384 run_norm
= 30.0f
/(float)w
->anim_run
->length
,
385 walk_adv
= vg_lerpf( walk_norm
, run_norm
, w
->move_speed
);
387 w
->walk_timer
+= walk_adv
* vg
.time_delta
;
391 w
->walk_timer
= 0.0f
;
394 float walk_norm
= (float)w
->anim_walk
->length
/30.0f
,
395 run_norm
= (float)w
->anim_run
->length
/30.0f
,
397 l
= vg_clampf( w
->blend_run
*15.0f
, 0.0f
, 1.0f
),
398 idle_walk
= vg_clampf( (w
->blend_run
-0.1f
)/(1.0f
-0.1f
), 0.0f
, 1.0f
);
401 skeleton_sample_anim( sk
, w
->anim_walk
, t
*walk_norm
, apose
);
402 skeleton_sample_anim( sk
, w
->anim_run
, t
*run_norm
, bpose
);
404 skeleton_lerp_pose( sk
, apose
, bpose
, l
, apose
);
407 skeleton_sample_anim( sk
, w
->anim_idle
, vg
.time
*0.1f
, bpose
);
408 skeleton_lerp_pose( sk
, apose
, bpose
, 1.0f
-idle_walk
, apose
);
411 skeleton_sample_anim( sk
, w
->anim_jump
, vg
.time
*0.6f
, bpose
);
412 skeleton_lerp_pose( sk
, apose
, bpose
, w
->blend_fly
, pose
);
414 /* Create transform matrix */
417 q_axis_angle( rot
, (v3f
){0.0f
,1.0f
,0.0f
}, -w
->state
.angles
[0]-VG_PIf
*0.5f
);
418 q_m3x3( rot
, rotation_mtx
);
420 rb_extrapolate_transform( &player
->rb
, transform
);
421 m3x3_copy( rotation_mtx
, transform
);
424 VG_STATIC
void player_walk_get_camera( player_interface
*player
,
425 player_attachment
*at
, camera
*cam
)
427 struct player_device_walk
*w
= at
->storage
;
428 struct player_avatar
*av
= player
->playeravatar
;
430 /* FIXME: viewpoint entity */
431 v3f vp
= {-0.1f
,1.8f
,0.0f
};
432 m4x3_mulv( av
->sk
.final_mtx
[ av
->id_head
-1 ], vp
, cam
->pos
);
433 v3_copy( w
->state
.angles
, cam
->angles
);
437 VG_STATIC
void player_walk_transport( player_interface
*player
,
438 player_attachment
*at
,
439 teleport_gate
*gate
)
441 struct player_device_walk
*w
= at
->storage
;
443 m4x3_mulv( gate
->transport
, player
->rb
.co
, player
->rb
.co
);
444 m3x3_mulv( gate
->transport
, player
->rb
.v
, player
->rb
.v
);
446 /* analytical rotation of yaw */
447 v3f fwd_dir
= { cosf(w
->state
.angles
[0]),
449 sinf(w
->state
.angles
[0])};
450 m3x3_mulv( gate
->transport
, fwd_dir
, fwd_dir
);
451 w
->state
.angles
[0] = atan2f( fwd_dir
[2], fwd_dir
[0] );
453 w
->state_gate_storage
= w
->state
;
454 player_apply_transport_to_cam( gate
->transport
);
457 VG_STATIC player_device player_device_walk
=
459 .pre_update
= player_walk_pre_update
,
460 .update
= player_walk_update
,
461 .post_update
= player_walk_post_update
,
462 .get_camera
= player_walk_get_camera
,
463 .debug_ui
= player_walk_ui
,
464 .bind
= player_walk_bind
,
465 .pose
= player_walk_pose
,
466 .gate_transport
= player_walk_transport
469 #endif /* PLAYER_DEVICE_WALK_H */