0559acdc2dce064965391a9794f702bb19189a69
[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
6 VG_STATIC float
7 k_walkspeed = 10.0f,
8 k_airspeed = 2.0f,
9 k_stopspeed = 4.0f,
10 k_walk_accel = 10.0f,
11 k_walk_air_accel = 7.0f,
12 k_walk_friction = 10.0f,
13 k_walk_step_height = 0.2f;
14
15 struct player_device_walk
16 {
17 rb_capsule collider;
18
19 struct
20 {
21 v3f angles;
22
23 enum walk_activity
24 {
25 k_walk_activity_air,
26 k_walk_activity_ground,
27 k_walk_activity_sleep
28 }
29 activity;
30 }
31 state;
32
33 enum mdl_surface_prop surface;
34 };
35
36 VG_STATIC void player_walk_pre_update( player_interface *player,
37 player_attachment *at )
38 {
39 struct player_device_walk *w = at->storage;
40 player_look( player, w->state.angles );
41
42 #if 0
43 v3f walk = { player->input_walkh->axis.value,
44 0.0f,
45 -player->input_walkv->axis.value };
46
47 v3_muls( walk, 10.0f * vg.time_delta, walk );
48
49 m3x3f m;
50 euler_m3x3( w->angles, m );
51 v3_muladds( player->rb.co, m[0], walk[0], player->rb.co );
52 v3_muladds( player->rb.co, m[1], walk[1], player->rb.co );
53 v3_muladds( player->rb.co, m[2], walk[2], player->rb.co );
54 #endif
55 }
56
57 VG_STATIC int player_walk_normal_standable( v3f n )
58 {
59 return n[1] > 0.70710678118f;
60 }
61
62 VG_STATIC void player_accelerate( v3f v, v3f movedir, float speed, float accel )
63 {
64 float currentspeed = v3_dot( v, movedir ),
65 addspeed = speed - currentspeed;
66
67 if( addspeed <= 0 )
68 return;
69
70 float accelspeed = accel * k_rb_delta * speed;
71
72 if( accelspeed > addspeed )
73 accelspeed = addspeed;
74
75 v3_muladds( v, movedir, accelspeed, v );
76 }
77
78 VG_STATIC void player_friction( v3f v )
79 {
80 float speed = v3_length( v ),
81 drop = 0.0f,
82 control = vg_maxf( speed, k_stopspeed );
83
84 if( speed < 0.04f )
85 return;
86
87 drop += control * k_walk_friction * k_rb_delta;
88
89 float newspeed = vg_maxf( 0.0f, speed - drop );
90 newspeed /= speed;
91
92 v3_muls( v, newspeed, v );
93 }
94
95 VG_STATIC void player_walk_update( player_interface *player,
96 player_attachment *at )
97 {
98 struct player_device_walk *w = at->storage;
99 w->collider.height = 2.0f;
100 w->collider.radius = 0.3f;
101
102 m4x3f mtx;
103 m3x3_identity( mtx );
104 v3_add( player->rb.co, (v3f){0.0f, 1.0f, 0.0f}, mtx[3] );
105
106 debug_capsule( mtx, w->collider.radius, w->collider.height, VG__WHITE );
107
108 rb_ct manifold[64];
109 int len;
110
111 float yaw = w->state.angles[0];
112
113 v3f forward_dir = { sinf(yaw), 0.0f, -cosf(yaw) };
114 v3f right_dir = { -forward_dir[2], 0.0f, forward_dir[0] };
115
116 v2f walk = { player->input_walkh->axis.value,
117 player->input_walkv->axis.value };
118
119 if( v2_length2(walk) > 0.001f )
120 v2_normalize_clamp( walk );
121
122 /*
123 * Collision detection
124 */
125 len = rb_capsule__scene( mtx, &w->collider, NULL,
126 &world.rb_geo.inf.scene, manifold );
127 rb_manifold_filter_coplanar( manifold, len, 0.01f );
128 len = rb_manifold_apply_filtered( manifold, len );
129
130 v3f surface_avg = { 0.0f, 0.0f, 0.0f };
131 w->state.activity = k_walk_activity_air;
132
133 for( int i=0; i<len; i++ )
134 {
135 struct contact *ct = &manifold[i];
136 rb_debug_contact( ct );
137
138 if( player_walk_normal_standable( ct->n ) )
139 {
140 w->state.activity = k_walk_activity_ground;
141 v3_add( surface_avg, ct->n, surface_avg );
142 }
143
144 rb_prepare_contact( ct );
145 }
146
147
148 /*
149 * Move & Friction
150 */
151 float accel_speed = 0.0f, nominal_speed = 0.0f;
152 v3f movedir;
153 v3_muls( right_dir, walk[0], movedir );
154 v3_muladds( movedir, forward_dir, walk[1], movedir );
155
156 if( w->state.activity == k_walk_activity_ground )
157 {
158 v3_normalize( surface_avg );
159
160 v3f tx, ty;
161 rb_tangent_basis( surface_avg, tx, ty );
162
163 if( v2_length2(walk) > 0.001f )
164 {
165 /* clip movement to the surface */
166 float d = v3_dot(surface_avg,movedir);
167 v3_muladds( movedir, surface_avg, -d, movedir );
168 }
169
170 accel_speed = k_walk_accel;
171 nominal_speed = k_walkspeed;
172
173 /* jump */
174 if( player->input_jump->button.value )
175 {
176 player->rb.v[1] = 5.0f;
177 w->state.activity = k_walk_activity_air;
178 accel_speed = k_walk_air_accel;
179 nominal_speed = k_airspeed;
180 }
181 else
182 {
183 player_friction( player->rb.v );
184
185 struct world_material *surface_mat = world_contact_material(manifold);
186 w->surface = surface_mat->info.surface_prop;
187 }
188 }
189 else
190 {
191 accel_speed = k_walk_air_accel;
192 nominal_speed = k_airspeed;
193 }
194
195 if( v2_length2(walk) > 0.001f )
196 {
197 vg_info( "%f %f\n", walk[0], walk[1] );
198 player_accelerate( player->rb.v, movedir, nominal_speed, accel_speed );
199 v3_normalize( movedir );
200 }
201
202 /*
203 * Resolve velocity constraints
204 */
205 for( int j=0; j<5; j++ )
206 {
207 for( int i=0; i<len; i++ )
208 {
209 struct contact *ct = &manifold[i];
210
211 /*normal */
212 float vn = -v3_dot( player->rb.v, ct->n );
213
214 float temp = ct->norm_impulse;
215 ct->norm_impulse = vg_maxf( temp + vn, 0.0f );
216 vn = ct->norm_impulse - temp;
217
218 v3_muladds( player->rb.v, ct->n, vn, player->rb.v );
219 }
220 }
221
222 /*
223 * Depenetrate
224 */
225 v3f dt;
226 v3_zero( dt );
227 for( int j=0; j<8; j++ )
228 {
229 for( int i=0; i<len; i++ )
230 {
231 struct contact *ct = &manifold[i];
232
233 float resolved_amt = v3_dot( ct->n, dt ),
234 remaining = (ct->p-k_penetration_slop) - resolved_amt,
235 apply = vg_maxf( remaining, 0.0f ) * 0.3f;
236
237 v3_muladds( dt, ct->n, apply, dt );
238 }
239 }
240 v3_add( dt, player->rb.co, player->rb.co );
241
242 /* TODO: Stepping......
243 *
244 * ideas; walkgrid style steps
245 */
246 #if 0
247 if( w->state.activity == k_walk_activity_ground )
248 {
249 /* step */
250 float max_dist = 0.4f;
251
252 v3f pa, pb;
253 v3_copy( player->rb.co, pa );
254 pa[1] += w->collider.radius + max_dist;
255
256 v3_muladds( pa, (v3f){0.0f,1.0f,0.0f}, -max_dist * 2.0f, pb );
257 vg_line( pa, pb, 0xff000000 );
258
259 v3f n;
260 float t;
261 if( spherecast_world( pa, pb, w->collider.radius, &t, n ) != -1 )
262 {
263 if( player_walk_normal_standable( n ) )
264 {
265 v3_lerp( pa, pb, t, player->rb.co );
266 player->rb.co[1] -= w->collider.radius;
267 }
268 }
269 }
270 #endif
271
272
273 /* integrate */
274 if( w->state.activity == k_walk_activity_air )
275 player->rb.v[1] += -k_gravity * k_rb_delta;
276
277 v3_muladds( player->rb.co, player->rb.v, k_rb_delta, player->rb.co );
278
279
280 v3_add( player->rb.co, (v3f){0.0f, 1.0f, 0.0f}, mtx[3] );
281 debug_capsule( mtx, w->collider.radius, w->collider.height, VG__GREEN );
282 }
283
284 VG_STATIC void player_walk_post_update( player_interface *player,
285 player_attachment *at )
286 {
287
288 }
289
290 VG_STATIC void player_walk_get_camera( player_interface *player,
291 player_attachment *at, camera *cam )
292 {
293 struct player_device_walk *w = at->storage;
294
295 float substep = vg_clampf( vg.accumulator / k_rb_delta, 0.0f, 1.0f );
296 v3f pos;
297 v3_muladds( player->rb.co, player->rb.v, k_rb_delta*substep, pos );
298
299 v3_add( pos, (v3f){0.0f,1.8f,0.0f}, cam->pos );
300 v3_copy( w->state.angles, cam->angles );
301 cam->fov = 90.0f;
302 }
303
304 VG_STATIC void player_walk_ui( player_interface *player,
305 player_attachment *at )
306 {
307 player_debugtext( 1, "V: %5.2f %5.2f %5.2f",player->rb.v[0],
308 player->rb.v[1],
309 player->rb.v[2] );
310 player_debugtext( 1, "CO: %5.2f %5.2f %5.2f",player->rb.co[0],
311 player->rb.co[1],
312 player->rb.co[2] );
313 }
314
315 player_device player_device_walk =
316 {
317 .pre_update = player_walk_pre_update,
318 .update = player_walk_update,
319 .post_update = player_walk_post_update,
320 .get_camera = player_walk_get_camera,
321 .debug_ui = player_walk_ui
322 };
323
324 #endif /* PLAYER_DEVICE_WALK_H */