the walk manifold is alive
[carveJwlIkooP6JGAAIwe30JlM.git] / player.h
1 #ifndef PLAYER_H
2 #define PLAYER_H
3
4 #include "common.h"
5 #include "character.h"
6
7 static int freecam = 0;
8 static float k_walkspeed = 2.0f;
9
10 static struct gplayer
11 {
12 /* Physics */
13 v3f co, v, a, v_last, m, bob;
14 v4f rot;
15 float vswitch, slip, slip_last,
16 reverse;
17
18 float iY; /* Yaw inertia */
19 int in_air, is_dead, on_board;
20
21 /* Input */
22 v2f joy_l;
23
24 v2f board_xy;
25 float grab;
26 float pitch;
27
28 v3f land_target;
29 v3f land_target_log[22];
30 u32 land_target_colours[22];
31 int land_log_count;
32 m3x3f vr;
33
34 m4x3f to_world, to_local;
35
36 struct character mdl;
37
38 v3f handl_target, handr_target,
39 handl, handr;
40
41 /* Camera */
42 float air_blend;
43
44 v3f camera_pos, smooth_localcam;
45 v2f angles;
46 m4x3f camera, camera_inverse;
47 }
48 player;
49
50 static void player_transform_update(void)
51 {
52 q_normalize( player.rot );
53 q_m3x3( player.rot, player.to_world );
54 v3_copy( player.co, player.to_world[3] );
55
56 m4x3_invert_affine( player.to_world, player.to_local );
57 }
58
59 static int reset_player( int argc, char const *argv[] )
60 {
61 v3_zero( player.co );
62
63 if( argc == 1 )
64 {
65 if( !strcmp( argv[0], "tutorial" ))
66 v3_copy( world.tutorial, player.co );
67 }
68
69 v3_copy( (v3f){ 0.0f, 0.0f, -0.2f }, player.v );
70 q_identity( player.rot );
71 player.vswitch = 1.0f;
72 player.slip_last = 0.0f;
73 player.is_dead = 0;
74 player.in_air = 1;
75 m3x3_identity( player.vr );
76
77 player.mdl.shoes[0] = 1;
78 player.mdl.shoes[1] = 1;
79
80 player_transform_update();
81 return 0;
82 }
83
84 static void player_mouseview(void)
85 {
86 static v2f mouse_last,
87 view_vel = { 0.0f, 0.0f };
88
89 if( vg_get_button_down( "primary" ) )
90 v2_copy( vg_mouse, mouse_last );
91 else if( vg_get_button( "primary" ) )
92 {
93 v2f delta;
94 v2_sub( vg_mouse, mouse_last, delta );
95 v2_copy( vg_mouse, mouse_last );
96
97 v2_muladds( view_vel, delta, 0.005f, view_vel );
98 }
99
100 v2_muls( view_vel, 0.7f, view_vel );
101 v2_add( view_vel, player.angles, player.angles );
102 player.angles[1] = vg_clampf( player.angles[1], -VG_PIf*0.5f, VG_PIf*0.5f );
103
104 }
105
106 static void player_freecam(void)
107 {
108 player_mouseview();
109
110 float movespeed = 25.0f;
111 v3f lookdir = { 0.0f, 0.0f, -1.0f },
112 sidedir = { 1.0f, 0.0f, 0.0f };
113
114 m3x3_mulv( player.camera, lookdir, lookdir );
115 m3x3_mulv( player.camera, sidedir, sidedir );
116
117 static v3f move_vel = { 0.0f, 0.0f, 0.0f };
118 if( vg_get_button( "forward" ) )
119 v3_muladds( move_vel, lookdir, ktimestep * movespeed, move_vel );
120 if( vg_get_button( "back" ) )
121 v3_muladds( move_vel, lookdir, ktimestep *-movespeed, move_vel );
122 if( vg_get_button( "left" ) )
123 v3_muladds( move_vel, sidedir, ktimestep *-movespeed, move_vel );
124 if( vg_get_button( "right" ) )
125 v3_muladds( move_vel, sidedir, ktimestep * movespeed, move_vel );
126
127 v3_muls( move_vel, 0.7f, move_vel );
128 v3_add( move_vel, player.camera_pos, player.camera_pos );
129 }
130
131 static void apply_gravity( v3f vel, float const timestep )
132 {
133 v3f gravity = { 0.0f, -9.6f, 0.0f };
134 v3_muladds( vel, gravity, timestep, vel );
135 }
136
137 static void player_start_air(void)
138 {
139 player.in_air = 1;
140
141 float pstep = ktimestep*10.0f;
142
143 float best_velocity_mod = 0.0f,
144 best_velocity_delta = -9999.9f;
145
146 v3f axis, vup;
147 m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, vup );
148 v3_cross( vup, player.v, axis );
149 v3_normalize( axis );
150 player.land_log_count = 0;
151
152 m3x3_identity( player.vr );
153
154 for( int m=-3;m<=12; m++ )
155 {
156 float vmod = ((float)m / 15.0f)*0.09f;
157
158 v3f pco, pco1, pv;
159 v3_copy( player.co, pco );
160 v3_copy( player.v, pv );
161
162 /*
163 * Try different 'rotations' of the velocity to find the best possible
164 * landing normal. This conserves magnitude at the expense of slightly
165 * unrealistic results
166 */
167
168 m3x3f vr;
169 v4f vr_q;
170
171 q_axis_angle( vr_q, axis, vmod );
172 q_m3x3( vr_q, vr );
173
174 m3x3_mulv( vr, pv, pv );
175 v3_muladds( pco, pv, ktimestep, pco );
176
177 for( int i=0; i<50; i++ )
178 {
179 v3_copy( pco, pco1 );
180 apply_gravity( pv, pstep );
181
182 m3x3_mulv( vr, pv, pv );
183 v3_muladds( pco, pv, pstep, pco );
184
185 ray_hit contact;
186 v3f vdir;
187
188 v3_sub( pco, pco1, vdir );
189 contact.dist = v3_length( vdir );
190 v3_divs( vdir, contact.dist, vdir);
191
192 if( ray_world( pco1, vdir, &contact ))
193 {
194 float land_delta = v3_dot( pv, contact.normal );
195 u32 scolour = (u8)(vg_minf(-land_delta * 2.0f, 255.0f));
196
197 /* Bias prediction towords ramps */
198 if( ray_hit_is_ramp( &contact ) )
199 {
200 land_delta *= 0.1f;
201 scolour |= 0x0000a000;
202 }
203
204 if( (land_delta < 0.0f) && (land_delta > best_velocity_delta) )
205 {
206 best_velocity_delta = land_delta;
207 best_velocity_mod = vmod;
208
209 v3_copy( contact.pos, player.land_target );
210
211 q_axis_angle( vr_q, axis, vmod*0.1f );
212 q_m3x3( vr_q, player.vr );
213 }
214
215 v3_copy( contact.pos,
216 player.land_target_log[player.land_log_count] );
217 player.land_target_colours[player.land_log_count] =
218 0xff000000 | scolour;
219
220 player.land_log_count ++;
221
222 break;
223 }
224 }
225 }
226
227 //v3_rotate( player.v, best_velocity_mod, axis, player.v );
228
229 return;
230 v3_muls( player.v, best_velocity_mod, player.v );
231 }
232
233 static int sample_if_resistant( v3f pos )
234 {
235 v3f ground;
236 v3_copy( pos, ground );
237 ground[1] += 4.0f;
238
239 ray_hit hit;
240 hit.dist = INFINITY;
241
242 if( ray_world( ground, (v3f){0.0f,-1.0f,0.0f}, &hit ))
243 {
244 v3f angle;
245 v3_copy( player.v, angle );
246 v3_normalize( angle );
247 float resistance = v3_dot( hit.normal, angle );
248
249 if( resistance < 0.25f )
250 {
251 v3_copy( hit.pos, pos );
252 return 1;
253 }
254 }
255
256 return 0;
257 }
258
259 static float stable_force( float current, float diff )
260 {
261 float new = current + diff;
262
263 if( new * current < 0.0f )
264 return 0.0f;
265
266 return new;
267 }
268
269 static void player_physics_ground(void)
270 {
271 /*
272 * Getting surface collision points,
273 * the contact manifold is a triangle for simplicity.
274 */
275 v3f contact_front, contact_back, contact_norm, vup, vside,
276 axis;
277
278 float klength = 0.65f;
279 m4x3_mulv( player.to_world, (v3f){ 0.15f,0.0f,-klength}, contact_norm );
280 m4x3_mulv( player.to_world, (v3f){-0.15f,0.0f,-klength}, contact_front );
281 m4x3_mulv( player.to_world, (v3f){ 0.00f,0.0f, klength}, contact_back );
282 m3x3_mulv( player.to_world, (v3f){ 0.0f, 1.0f, 0.0f}, vup );
283 m3x3_mulv( player.to_world, (v3f){ 1.0f, 0.0f, 0.0f}, vside );
284
285 v3f cn0, cn1, cn2;
286
287 int contact_count =
288 sample_if_resistant( contact_front ) +
289 sample_if_resistant( contact_back ) +
290 sample_if_resistant( contact_norm );
291
292 if( contact_count < 3 )
293 {
294 player_start_air();
295 return;
296 }
297
298 v3f norm;
299 v3f v0, v1;
300 v3_sub( contact_norm, contact_front, v0 );
301 v3_sub( contact_back, contact_front, v1 );
302 v3_cross( v1, v0, norm );
303 v3_normalize( norm );
304
305 vg_line( contact_norm, contact_front, 0xff00ff00 );
306 vg_line( contact_back, contact_front, 0xff0000ff );
307
308 /* Surface alignment */
309 float angle = v3_dot( vup, norm );
310 v3_cross( vup, norm, axis );
311
312 if( angle < 0.999f )
313 {
314 v4f correction;
315 q_axis_angle( correction, axis, acosf(angle) );
316 q_mul( correction, player.rot, player.rot );
317 }
318
319 float resistance = v3_dot( norm, player.v );
320 if( resistance >= 0.0f )
321 {
322 player_start_air();
323 return;
324 }
325 else
326 {
327 v3_muladds( player.v, norm, -resistance, player.v );
328 }
329
330 /* This is where velocity integration used to be */
331
332 float slip = 0.0f;
333
334 player.co[1] = (contact_front[1]+contact_back[1])*0.5f;
335
336 v3f vel;
337 m3x3_mulv( player.to_local, player.v, vel );
338
339 /* Calculate local forces */
340
341 if( fabsf(vel[2]) > 0.01f )
342 slip = fabsf(-vel[0] / vel[2]) * vg_signf(vel[0]);
343
344 if( fabsf( slip ) > 1.2f )
345 slip = vg_signf( slip ) * 1.2f;
346 player.slip = slip;
347 player.reverse = -vg_signf(vel[2]);
348
349 float substep = ktimestep * 0.2f;
350 float fwd_resistance = (vg_get_button( "break" )? 5.0f: 0.02f) * -substep;
351
352 for( int i=0; i<5; i++ )
353 {
354 vel[2] = stable_force( vel[2], vg_signf( vel[2] ) * fwd_resistance );
355 vel[0] = stable_force( vel[0], vg_signf( vel[0] ) * -7.0f *substep );
356 }
357
358 static double start_push = 0.0;
359 if( vg_get_button_down( "push" ) )
360 start_push = vg_time;
361
362 if( !vg_get_button("break") && vg_get_button( "push" ) )
363 {
364 float const k_maxpush = 16.0f,
365 k_pushaccel = 5.0f;
366
367 float cycle_time = vg_time-start_push,
368 amt = k_pushaccel * (sinf( cycle_time * 8.0f )*0.5f+0.5f)*ktimestep,
369 current = v3_length( vel ),
370 new_vel = vg_minf( current + amt, k_maxpush );
371 new_vel -= vg_minf(current, k_maxpush);
372 vel[2] -= new_vel * player.reverse;
373 }
374
375 m3x3_mulv( player.to_world, vel, player.v );
376
377 if( vg_get_button( "yawl" ) )
378 player.iY += 3.6f * ktimestep;
379 if( vg_get_button( "yawr" ) )
380 player.iY -= 3.6f * ktimestep;
381
382 float steer = vg_get_axis( "horizontal" );
383 player.iY -= vg_signf(steer)*powf(steer,2.0f) * 1.5f * ktimestep;
384
385 /* Too much lean and it starts to look like a snowboard here */
386 v2_lerp( player.board_xy, (v2f){ slip*0.25f, 0.0f },
387 ktimestep*5.0f, player.board_xy);
388 }
389
390 static void draw_cross(v3f pos,u32 colour, float scale)
391 {
392 v3f p0, p1;
393 v3_add( (v3f){ scale,0.0f,0.0f}, pos, p0 );
394 v3_add( (v3f){-scale,0.0f,0.0f}, pos, p1 );
395 vg_line( p0, p1, colour );
396 v3_add( (v3f){0.0f, scale,0.0f}, pos, p0 );
397 v3_add( (v3f){0.0f,-scale,0.0f}, pos, p1 );
398 vg_line( p0, p1, colour );
399 v3_add( (v3f){0.0f,0.0f, scale}, pos, p0 );
400 v3_add( (v3f){0.0f,0.0f,-scale}, pos, p1 );
401 vg_line( p0, p1, colour );
402 }
403
404 static void player_physics_air(void)
405 {
406 m3x3_mulv( player.vr, player.v, player.v );
407 for( int i=0; i<player.land_log_count; i++ )
408 draw_cross( player.land_target_log[i], player.land_target_colours[i], 1);
409
410 draw_cross( player.land_target, 0xff0000ff, 1 );
411
412 v3f ground_pos;
413 v3_copy( player.co, ground_pos );
414 ground_pos[1] += 4.0f;
415
416 ray_hit hit;
417 hit.dist = INFINITY;
418 if( ray_world( ground_pos, (v3f){0.0f,-1.0f,0.0f}, &hit ))
419 {
420 if( hit.pos[1] > player.co[1] )
421 {
422 player.in_air = 0;
423
424 if( !ray_hit_is_ramp( &hit ) )
425 {
426 player.is_dead = 1;
427 character_ragdoll_copypose( &player.mdl, player.v );
428 }
429
430 return;
431 }
432 }
433
434 /* Prediction
435 *
436 * TODO: Find best landing surface and guide player towords it
437 */
438 float pstep = ktimestep*10.0f;
439
440 v3f pco, pco1, pv;
441 v3_copy( player.co, pco );
442 v3_copy( player.v, pv );
443
444 float time_to_impact = 0.0f;
445 float limiter = 1.0f;
446
447 for( int i=0; i<50; i++ )
448 {
449 v3_copy( pco, pco1 );
450 apply_gravity( pv, pstep );
451 v3_muladds( pco, pv, pstep, pco );
452
453 //vg_line( pco, pco1, i&0x1?0xff000000:0xffffffff );
454
455 ray_hit contact;
456 v3f vdir;
457
458 v3_sub( pco, pco1, vdir );
459 contact.dist = v3_length( vdir );
460 v3_divs( vdir, contact.dist, vdir);
461
462 float orig_dist = contact.dist;
463 if( ray_world( pco1, vdir, &contact ))
464 {
465 v3f localup;
466 m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, localup );
467
468 float angle = v3_dot( localup, contact.normal );
469 v3f axis;
470 v3_cross( localup, contact.normal, axis );
471
472 time_to_impact += (contact.dist/orig_dist)*pstep;
473 limiter = vg_minf( 5.0f, time_to_impact )/5.0f;
474 limiter = 1.0f-limiter;
475 limiter *= limiter;
476 limiter = 1.0f-limiter;
477
478 if( angle < 0.99f )
479 {
480 v4f correction;
481 q_axis_angle( correction, axis, acosf(angle)*0.05f*(1.0f-limiter) );
482 q_mul( correction, player.rot, player.rot );
483 }
484
485 draw_cross( contact.pos, 0xffff0000, 1 );
486 break;
487 }
488 time_to_impact += pstep;
489 }
490
491 player.iY -= vg_get_axis( "horizontal" ) * 3.6f * ktimestep;
492 {
493
494 float iX = vg_get_axis( "vertical" ) * 3.6f * limiter * ktimestep;
495 static float siX = 0.0f;
496 siX = vg_lerpf( siX, iX, 0.3f );
497
498 v4f rotate;
499 v3f vside;
500
501 m3x3_mulv( player.to_world, (v3f){1.0f,0.0f,0.0f}, vside );
502
503 q_axis_angle( rotate, vside, siX );
504 q_mul( rotate, player.rot, player.rot );
505 }
506
507 v2f target = {0.0f,0.0f};
508 v2_muladds( target, (v2f){ vg_get_axis("h1"), vg_get_axis("v1") },
509 player.grab, target );
510 v2_lerp( player.board_xy, target, ktimestep*3.0f, player.board_xy );
511 }
512
513 static void player_do_motion(void)
514 {
515 float horizontal = vg_get_axis("horizontal"),
516 vertical = vg_get_axis("vertical");
517
518 player.joy_l[0] = vg_signf(horizontal) * powf( horizontal, 2.0f );
519 player.joy_l[1] = vg_signf(vertical) * powf( vertical, 2.0f );
520
521 if( player.in_air )
522 player_physics_air();
523
524 if( !player.in_air )
525 player_physics_ground();
526
527 /* Integrate velocity */
528 v3f prevco;
529 v3_copy( player.co, prevco );
530
531 apply_gravity( player.v, ktimestep );
532 v3_muladds( player.co, player.v, ktimestep, player.co );
533
534 /* Integrate inertia */
535 v4f rotate; v3f vup = {0.0f,1.0f,0.0f};
536 m3x3_mulv( player.to_world, vup, vup );
537
538 static float siY = 0.0f;
539
540 float lerpq = player.in_air? 0.04f: 0.3f;
541 siY = vg_lerpf( siY, player.iY, lerpq );
542
543 q_axis_angle( rotate, vup, siY );
544 q_mul( rotate, player.rot, player.rot );
545
546 player.iY = 0.0f; /* temp */
547
548 #if 0
549 /* GATE COLLISION */
550 if( gate_intersect( &gate_a, player.co, prevco ) )
551 {
552 teleport_gate *gate = &gate_a;
553
554 m4x3f transport;
555 m4x3_mul( gate->other->to_world, gate->to_local, transport );
556 m4x3_mulv( transport, player.co, player.co );
557 m3x3_mulv( transport, player.v, player.v );
558 m3x3_mulv( transport, player.v_last, player.v_last );
559 m3x3_mulv( transport, player.m, player.m );
560 m3x3_mulv( transport, player.bob, player.bob );
561
562 v4f transport_rotation;
563 m3x3_q( transport, transport_rotation );
564 q_mul( transport_rotation, player.rot, player.rot );
565 }
566 #endif
567
568 /* Camera and character */
569 player_transform_update();
570
571 player.angles[0] = atan2f( player.v[0], -player.v[2] );
572 player.angles[1] = atan2f( -player.v[1], sqrtf(player.v[0]*player.v[0]+
573 player.v[2]*player.v[2]) ) * 0.3f;
574
575 player.air_blend = vg_lerpf( player.air_blend, player.in_air, 0.04f );
576 v3_muladds( player.camera_pos, player.v, -0.05f*player.air_blend,
577 player.camera_pos );
578 }
579
580 /*
581 * Get a sample at this pole location, will return 1 if the sample is valid,
582 * and pos will be updated to be the intersection location.
583 */
584 static int player_walkgrid_samplepole( u32 *geo, int len, v3f pos )
585 {
586 v3f p1;
587 v3_copy( pos, p1 );
588 p1[1] -= 10.0f;
589
590 vg_line( pos, p1, 0x20ffffff );
591
592 v3f sample_pos;
593 v3_copy(pos, sample_pos);
594
595 v3f vdir = {0.0f,-1.0f,0.0f};
596 int count = 0;
597
598 ray_hit hit;
599 hit.dist = INFINITY;
600 for( int i=0; i<len; i++ )
601 {
602 u32 *tri = &world.geo.indices[ geo[i] ];
603 count += bvh_ray_tri( &world.geo, tri, sample_pos, vdir, &hit );
604 }
605
606 if( count )
607 {
608 v3f v0, v1;
609 float *pa = world.geo.verts[hit.tri[0]].co,
610 *pb = world.geo.verts[hit.tri[1]].co,
611 *pc = world.geo.verts[hit.tri[2]].co;
612
613 v3_sub( pa, pb, v0 );
614 v3_sub( pc, pb, v1 );
615 v3_cross( v1, v0, hit.normal );
616 v3_normalize( hit.normal );
617 v3_muladds( sample_pos, vdir, hit.dist, pos );
618
619 draw_cross( pos, 0xff00ff00, 0.05f );
620 return count;
621 }
622 else
623 return 0;
624 }
625
626 static void player_walkgrid_clip(u32 *geo, int len, v3f pos, v3f dir, v3f clip)
627 {
628 float max_dist = 0.0f;
629 v3f tri[3];
630 v3f perp;
631 v3_cross( dir,(v3f){0.0f,1.0f,0.0f},perp );
632 v3_copy( pos, clip );
633
634 for( int i=0; i<len; i++ )
635 {
636 u32 *ptri = &world.geo.indices[ geo[i] ];
637 for( int j=0; j<3; j++ )
638 v3_copy( world.geo.verts[ptri[j]].co, tri[j] );
639
640 for( int k=0; k<3; k++ )
641 {
642 int ia = k,
643 ib = (k+1)%3;
644
645 v3f v0, v1;
646 v3_sub( tri[ia], pos, v0 );
647 v3_sub( tri[ib], pos, v1 );
648
649 if( (dir[2]*v0[0] - dir[0]*v0[2]) *
650 (dir[2]*v1[0] - dir[0]*v1[2]) < 0.0f )
651 {
652 float da = v3_dot(v0,perp),
653 db = v3_dot(v1,perp),
654 d = da-db,
655 qa = da/d;
656
657 v3f p0;
658 v3_muls( v1, qa, p0 );
659 v3_muladds( p0, v0, 1.0f-qa, p0 );
660
661 float h = v3_dot(p0,dir)/v3_dot(dir,dir);
662
663 if( h >= max_dist && h <= 1.0f )
664 {
665 max_dist = h;
666 float l = 1.0f/v3_length(dir);
667 v3_muls( p0, l, clip );
668 }
669 }
670 }
671 }
672 }
673
674 #define WALKGRID_SIZE 8
675 struct walkgrid
676 {
677 struct grid_sample
678 {
679 int valid;
680 v3f clip[2];
681 v3f pos;
682 }
683 samples[WALKGRID_SIZE][WALKGRID_SIZE];
684 u32 geo[256];
685
686 boxf region;
687
688 float move; /* Current amount of movement we have left to apply */
689 v2f dir; /* The movement delta */
690 v2i cell_id;/* Current cell */
691 v2f pos; /* Local position (in cell) */
692 float h;
693 };
694
695 static const struct conf
696 {
697 struct confedge
698 {
699 /* i: sample index
700 * d: data index
701 * a: axis index
702 * o: the 'other' point to do a A/B test with
703 * if its -1, all AB is done.
704 *
705 * TODO: Check the major edge against point (for the double cases)
706 */
707 int i0, i1,
708 d0, d1,
709 a0, a1,
710 o0, o1;
711 }
712 edges[2];
713 int edge_count;
714 }
715 k_walkgrid_configs[16] = {
716 {{},0},
717 {{{ 3,3, 3,0, 1,0, -1,-1 }}, 1},
718 {{{ 2,2, 1,3, 0,1, -1,-1 }}, 1},
719 {{{ 2,3, 1,0, 0,0, 3,-1 }}, 1},
720
721 {{{ 1,1, 0,1, 1,0, -1,-1 }}, 1},
722 {{{ 3,3, 3,0, 1,0, -1,-1 },
723 { 1,1, 0,1, 1,0, -1,-1 }}, 2},
724 {{{ 1,2, 0,3, 1,1, 2,-1 }}, 1},
725 {{{ 1,3, 0,0, 1,0, 2, 2 }}, 1},
726
727 {{{ 0,0, 0,0, 0,1, -1,-1 }}, 1},
728 {{{ 3,0, 3,0, 1,1, 0,-1 }}, 1},
729 {{{ 2,2, 1,3, 0,1, -1,-1 },
730 { 0,0, 0,0, 0,1, -1,-1 }}, 2},
731 {{{ 2,0, 1,0, 0,1, 3, 3 }}, 1},
732
733 {{{ 0,1, 0,1, 0,0, 1,-1 }}, 1},
734 {{{ 3,1, 3,1, 1,0, 0, 0 }}, 1},
735 {{{ 0,2, 0,3, 0,1, 1, 1 }}, 1},
736 {{},0},
737 };
738
739 /*
740 * Get a buffer of edges from cell location
741 */
742 static const struct conf *player_walkgrid_conf( struct walkgrid *wg,
743 v2i cell,
744 struct grid_sample *corners[4] )
745 {
746 corners[0] = &wg->samples[cell[1] ][cell[0] ];
747 corners[1] = &wg->samples[cell[1]+1][cell[0] ];
748 corners[2] = &wg->samples[cell[1]+1][cell[0]+1];
749 corners[3] = &wg->samples[cell[1] ][cell[0]+1];
750
751 u32 config = (corners[0]->valid<<3) | (corners[1]->valid<<2) |
752 (corners[2]->valid<<1) | corners[3]->valid;
753
754 return &k_walkgrid_configs[ config ];
755 }
756
757 float const k_gridscale = 0.5f;
758
759 static void player_walkgrid_floor(v3f pos)
760 {
761 v3_muls( pos, 1.0f/k_gridscale, pos );
762 v3_floor( pos, pos );
763 v3_muls( pos, k_gridscale, pos );
764 }
765
766 /*
767 * Computes the barycentric coordinate of location on a triangle (vertical),
768 * then sets the Y position to the interpolation of the three points
769 */
770 static void player_walkgrid_stand_tri( v3f a, v3f b, v3f c, v3f pos )
771 {
772 v3f v0,v1,v2;
773 v3_sub( b, a, v0 );
774 v3_sub( c, a, v1 );
775 v3_sub( pos, a, v2 );
776
777 float d = v0[0]*v1[2] - v1[0]*v0[2],
778 v = (v2[0]*v1[2] - v1[0]*v2[2]) / d,
779 w = (v0[0]*v2[2] - v2[0]*v0[2]) / d,
780 u = 1.0f - v - w;
781
782 vg_line( pos, a, 0xffff0000 );
783 vg_line( pos, b, 0xff00ff00 );
784 vg_line( pos, c, 0xff0000ff );
785 pos[1] = u*a[1] + v*b[1] + w*c[1];
786 }
787
788 /*
789 * Get the minimum time value of pos+dir until a cell edge
790 *
791 * t[0] -> t[3] are the individual time values
792 * t[5] & t[6] are the maximum axis values
793 * t[6] is the minimum value
794 *
795 */
796 static void player_walkgrid_min_cell( float t[7], v2f pos, v2f dir )
797 {
798 v2f frac = { 1.0f/dir[0], 1.0f/dir[1] };
799
800 t[0] = 999.9f;
801 t[1] = 999.9f;
802 t[2] = 999.9f;
803 t[3] = 999.9f;
804
805 if( fabsf(dir[0]) > 0.0001f )
806 {
807 t[0] = (0.0f-pos[0]) * frac[0];
808 t[1] = (1.0f-pos[0]) * frac[0];
809 }
810 if( fabsf(dir[1]) > 0.0001f )
811 {
812 t[2] = (0.0f-pos[1]) * frac[1];
813 t[3] = (1.0f-pos[1]) * frac[1];
814 }
815
816 t[4] = vg_maxf(t[0],t[1]);
817 t[5] = vg_maxf(t[2],t[3]);
818 t[6] = vg_minf(t[4],t[5]);
819 }
820
821 static void player_walkgrid_iter(struct walkgrid *wg, int iter)
822 {
823
824 /*
825 * For each walkgrid iteration we are stepping through cells and determining
826 * the intersections with the grid, and any edges that are present
827 */
828
829 #if 0
830 if( wg->cell_id[0] < 0 || wg->cell_id[0] >= WALKGRID_SIZE-1 ||
831 wg->cell_id[1] < 0 || wg->cell_id[1] >= WALKGRID_SIZE-1 )
832 {
833 /*
834 * This condition should never be reached if the grid size is big
835 * enough
836 */
837 wg->move = -1.0f;
838 return;
839 }
840 #endif
841
842 u32 icolours[] = { 0xffff00ff, 0xff00ffff, 0xffffff00 };
843
844 v3f pa, pb, pc, pd, pl0, pl1;
845 pa[0] = wg->region[0][0] + (float)wg->cell_id[0] *k_gridscale;
846 pa[1] = (wg->region[0][1] + wg->region[1][1]) * 0.5f + k_gridscale;
847 pa[2] = wg->region[0][2] + (float)wg->cell_id[1] *k_gridscale;
848 pb[0] = pa[0];
849 pb[1] = pa[1];
850 pb[2] = pa[2] + k_gridscale;
851 pc[0] = pa[0] + k_gridscale;
852 pc[1] = pa[1];
853 pc[2] = pa[2] + k_gridscale;
854 pd[0] = pa[0] + k_gridscale;
855 pd[1] = pa[1];
856 pd[2] = pa[2];
857 #if 0
858 vg_line( pa, pb, 0xff00ffff );
859 vg_line( pb, pc, 0xff00ffff );
860 vg_line( pc, pd, 0xff00ffff );
861 vg_line( pd, pa, 0xff00ffff );
862 #endif
863 pl0[0] = pa[0] + wg->pos[0]*k_gridscale;
864 pl0[1] = pa[1];
865 pl0[2] = pa[2] + wg->pos[1]*k_gridscale;
866
867 /*
868 * If there are edges present, we need to create a 'substep' event, where
869 * we find the intersection point, find the fully resolved position,
870 * then the new pos dir is the intersection->resolution
871 *
872 * the resolution is applied in non-discretized space in order to create a
873 * suitable vector for finding outflow, we want it to leave the cell so it
874 * can be used by the quad
875 */
876
877 v2f pos, dir;
878 v2_copy( wg->pos, pos );
879 v2_muls( wg->dir, wg->move, dir );
880
881 struct grid_sample *corners[4];
882 v2f corners2d[4] = {{0.0f,0.0f},{0.0f,1.0f},{1.0f,1.0f},{1.0f,0.0f}};
883 const struct conf *conf = player_walkgrid_conf( wg, wg->cell_id, corners );
884
885 float t[7];
886 player_walkgrid_min_cell( t, pos, dir );
887
888 for( int i=0; i<conf->edge_count; i++ )
889 {
890 const struct confedge *edge = &conf->edges[i];
891
892 v2f e0, e1, n, r, target, res, tangent;
893 e0[0] = corners2d[edge->i0][0] + corners[edge->d0]->clip[edge->a0][0];
894 e0[1] = corners2d[edge->i0][1] + corners[edge->d0]->clip[edge->a0][2];
895 e1[0] = corners2d[edge->i1][0] + corners[edge->d1]->clip[edge->a1][0];
896 e1[1] = corners2d[edge->i1][1] + corners[edge->d1]->clip[edge->a1][2];
897
898 v3f pe0 = { pa[0] + e0[0]*k_gridscale,
899 pa[1],
900 pa[2] + e0[1]*k_gridscale };
901 v3f pe1 = { pa[0] + e1[0]*k_gridscale,
902 pa[1],
903 pa[2] + e1[1]*k_gridscale };
904
905 v2_sub( e1, e0, tangent );
906 n[0] = -tangent[1];
907 n[1] = tangent[0];
908 v2_normalize( n );
909
910 /*
911 * If we find ourselfs already penetrating the edge, move back out a
912 * little
913 */
914 v2_sub( e0, pos, r );
915 float p1 = v2_dot(r,n);
916
917 if( -p1 < 0.0001f )
918 {
919 v2_muladds( pos, n, p1+0.0001f, pos );
920 v2_copy( pos, wg->pos );
921 v3f p_new = { pa[0] + pos[0]*k_gridscale,
922 pa[1],
923 pa[2] + pos[1]*k_gridscale };
924 v3_copy( p_new, pl0 );
925 }
926
927 v2_add( pos, dir, target );
928
929 v2f v1, v2, v3;
930 v2_sub( e0, pos, v1 );
931 v2_sub( target, pos, v2 );
932
933 v2_copy( n, v3 );
934
935 v2_sub( e0, target, r );
936 float p = v2_dot(r,n),
937 t1 = v2_dot(v1,v3)/v2_dot(v2,v3);
938
939 if( t1 < t[6] && t1 > 0.0f && -p < 0.001f )
940 {
941 v2_muladds( target, n, p+0.0001f, res );
942
943 v2f intersect;
944 v2_muladds( pos, dir, t1, intersect );
945 v2_copy( intersect, pos );
946 v2_sub( res, intersect, dir );
947
948 v3f p_res = { pa[0] + res[0]*k_gridscale,
949 pa[1],
950 pa[2] + res[1]*k_gridscale };
951 v3f p_int = { pa[0] + intersect[0]*k_gridscale,
952 pa[1],
953 pa[2] + intersect[1]*k_gridscale };
954
955 vg_line( pl0, p_int, icolours[iter%3] );
956 v3_copy( p_int, pl0 );
957 v2_copy( pos, wg->pos );
958
959 player_walkgrid_min_cell( t, pos, dir );
960 }
961 }
962
963 /*
964 * Compute intersection with grid cell moving outwards
965 */
966 t[6] = vg_minf( t[6], 1.0f );
967
968 pl1[0] = pl0[0] + dir[0]*k_gridscale*t[6];
969 pl1[1] = pl0[1];
970 pl1[2] = pl0[2] + dir[1]*k_gridscale*t[6];
971 vg_line( pl0, pl1, icolours[iter%3] );
972
973 if( t[6] < 1.0f )
974 {
975 /*
976 * To figure out what t value created the clip so we know which edge
977 * to wrap around
978 */
979
980 if( t[4] < t[5] )
981 {
982 wg->pos[1] = pos[1] + dir[1]*t[6];
983
984 if( t[0] > t[1] ) /* left edge */
985 {
986 wg->pos[0] = 0.9999f;
987 wg->cell_id[0] --;
988
989 if( wg->cell_id[0] == 0 )
990 wg->move = -1.0f;
991 }
992 else /* Right edge */
993 {
994 wg->pos[0] = 0.0001f;
995 wg->cell_id[0] ++;
996
997 if( wg->cell_id[0] == WALKGRID_SIZE-2 )
998 wg->move = -1.0f;
999 }
1000 }
1001 else
1002 {
1003 wg->pos[0] = pos[0] + dir[0]*t[6];
1004
1005 if( t[2] > t[3] ) /* bottom edge */
1006 {
1007 wg->pos[1] = 0.9999f;
1008 wg->cell_id[1] --;
1009
1010 if( wg->cell_id[1] == 0 )
1011 wg->move = -1.0f;
1012 }
1013 else /* top edge */
1014 {
1015 wg->pos[1] = 0.0001f;
1016 wg->cell_id[1] ++;
1017
1018 if( wg->cell_id[1] == WALKGRID_SIZE-2 )
1019 wg->move = -1.0f;
1020 }
1021 }
1022
1023 wg->move -= t[6];
1024 }
1025 else
1026 {
1027 v2_muladds( wg->pos, dir, wg->move, wg->pos );
1028 wg->move = 0.0f;
1029 }
1030 }
1031
1032 static void player_walkgrid_stand_cell(struct walkgrid *wg)
1033 {
1034 /*
1035 * NOTE: as opposed to the other function which is done in discretized space
1036 * this use a combination of both.
1037 */
1038
1039 v3f world;
1040 world[0] = wg->region[0][0]+((float)wg->cell_id[0]+wg->pos[0])*k_gridscale;
1041 world[2] = wg->region[0][2]+((float)wg->cell_id[1]+wg->pos[1])*k_gridscale;
1042
1043 struct grid_sample *corners[4];
1044 const struct conf *conf = player_walkgrid_conf( wg, wg->cell_id, corners );
1045
1046 if( conf != k_walkgrid_configs )
1047 {
1048 if( conf->edge_count == 0 )
1049 {
1050 v3f v0;
1051
1052 /* Split the basic quad along the shortest diagonal */
1053 if( fabsf(corners[2]->pos[1] - corners[0]->pos[1]) <
1054 fabsf(corners[3]->pos[1] - corners[1]->pos[1]) )
1055 {
1056 vg_line( corners[2]->pos, corners[0]->pos, 0xffaaaaaa );
1057
1058 if( wg->pos[0] > wg->pos[1] )
1059 player_walkgrid_stand_tri( corners[0]->pos,
1060 corners[3]->pos,
1061 corners[2]->pos, world );
1062 else
1063 player_walkgrid_stand_tri( corners[0]->pos,
1064 corners[2]->pos,
1065 corners[1]->pos, world );
1066 }
1067 else
1068 {
1069 vg_line( corners[3]->pos, corners[1]->pos, 0xffaaaaaa );
1070
1071 if( wg->pos[0] < 1.0f-wg->pos[1] )
1072 player_walkgrid_stand_tri( corners[0]->pos,
1073 corners[3]->pos,
1074 corners[1]->pos, world );
1075 else
1076 player_walkgrid_stand_tri( corners[3]->pos,
1077 corners[2]->pos,
1078 corners[1]->pos, world );
1079 }
1080 }
1081 else
1082 {
1083 for( int i=0; i<conf->edge_count; i++ )
1084 {
1085 const struct confedge *edge = &conf->edges[i];
1086
1087 v3f p0, p1;
1088 v3_muladds( corners[edge->i0]->pos,
1089 corners[edge->d0]->clip[edge->a0], k_gridscale, p0 );
1090 v3_muladds( corners[edge->i1]->pos,
1091 corners[edge->d1]->clip[edge->a1], k_gridscale, p1 );
1092
1093 /*
1094 * Find penetration distance between player position and the edge
1095 */
1096
1097 v2f normal = { -(p1[2]-p0[2]), p1[0]-p0[0] },
1098 rel = { world[0]-p0[0], world[2]-p0[2] };
1099
1100 if( edge->o0 == -1 )
1101 {
1102 /* No subregions (default case), just use triangle created by
1103 * i0, e0, e1 */
1104 player_walkgrid_stand_tri( corners[edge->i0]->pos,
1105 p0,
1106 p1, world );
1107 }
1108 else
1109 {
1110 /*
1111 * Test if we are in the first region, which is
1112 * edge.i0, edge.e0, edge.o0,
1113 */
1114 v3f v0, ref;
1115 v3_sub( p0, corners[edge->o0]->pos, ref );
1116 v3_sub( world, corners[edge->o0]->pos, v0 );
1117
1118 vg_line( corners[edge->o0]->pos, p0, 0xffffff00 );
1119 vg_line( corners[edge->o0]->pos, world, 0xff000000 );
1120
1121 if( ref[0]*v0[2] - ref[2]*v0[0] < 0.0f )
1122 {
1123 player_walkgrid_stand_tri( corners[edge->i0]->pos,
1124 p0,
1125 corners[edge->o0]->pos, world );
1126 }
1127 else
1128 {
1129 if( edge->o1 == -1 )
1130 {
1131 /*
1132 * No other edges mean we just need to use the opposite
1133 *
1134 * e0, e1, o0 (in our case, also i1)
1135 */
1136 player_walkgrid_stand_tri( p0,
1137 p1,
1138 corners[edge->o0]->pos, world );
1139 }
1140 else
1141 {
1142 /*
1143 * Note: this v0 calculation can be ommited with the
1144 * current tileset.
1145 *
1146 * the last two triangles we have are:
1147 * e0, e1, o1
1148 * and
1149 * e1, i1, o1
1150 */
1151 v3_sub( p1, corners[edge->o1]->pos, ref );
1152 v3_sub( world, corners[edge->o1]->pos, v0 );
1153 vg_line( corners[edge->o1]->pos, p1, 0xff00ffff );
1154
1155 if( ref[0]*v0[2] - ref[2]*v0[0] < 0.0f )
1156 {
1157 player_walkgrid_stand_tri( p0,
1158 p1,
1159 corners[edge->o1]->pos,
1160 world );
1161 }
1162 else
1163 {
1164 player_walkgrid_stand_tri( p1,
1165 corners[edge->i1]->pos,
1166 corners[edge->o1]->pos,
1167 world );
1168 }
1169 }
1170 }
1171 }
1172 }
1173 }
1174 }
1175
1176 v3_copy( world, player.co );
1177 }
1178
1179 static void player_walkgrid_getsurface(void)
1180 {
1181 float const k_stepheight = 0.5f;
1182 float const k_miny = 0.6f;
1183 float const k_height = 1.78f;
1184 float const k_region_size = (float)WALKGRID_SIZE/2.0f * k_gridscale;
1185
1186 struct walkgrid wg;
1187
1188 v3f cell;
1189 v3_copy( player.co, cell );
1190 player_walkgrid_floor( cell );
1191
1192 v3_muladds( cell, (v3f){-1.0f,-1.0f,-1.0f}, k_region_size, wg.region[0] );
1193 v3_muladds( cell, (v3f){ 1.0f, 1.0f, 1.0f}, k_region_size, wg.region[1] );
1194
1195 int tri_count = bvh_select_triangles( &world.geo, wg.region, wg.geo, 256 );
1196
1197 v3f tri[3];
1198 for( int i=0; i<tri_count; i++ )
1199 {
1200 for( int j=0; j<3; j++ )
1201 v3_copy( world.geo.verts[ world.geo.indices[wg.geo[i]+j]].co, tri[j] );
1202
1203 vg_line( tri[0], tri[1], 0xffa2ff30 );
1204 vg_line( tri[1], tri[2], 0xffa2ff30 );
1205 vg_line( tri[2], tri[0], 0xffa2ff30 );
1206 }
1207
1208 /* Get surface samples
1209 *
1210 * TODO: Replace this with a spiral starting from the player position
1211 */
1212 for( int y=0; y<WALKGRID_SIZE; y++ )
1213 {
1214 for( int x=0; x<WALKGRID_SIZE; x++ )
1215 {
1216 struct grid_sample *s = &wg.samples[y][x];
1217 v3_muladds( wg.region[0], (v3f){ x, 0, y }, k_gridscale, s->pos );
1218 s->pos[1] = player.co[1] + k_height;
1219
1220 s->valid = player_walkgrid_samplepole( wg.geo,tri_count,s->pos )? 1: 0;
1221 }
1222 }
1223
1224 /*
1225 * Calculate h+v clipping distances.
1226 * Distances are stored in A always, so you know that if the sample is
1227 * invalid, this signifies the start of the manifold as opposed to the
1228 * extent or bounds of it.
1229 */
1230 for( int i=0; i<2; i++ )
1231 {
1232 for( int x=0; x<WALKGRID_SIZE; x++ )
1233 {
1234 for( int z=0; z<WALKGRID_SIZE-1; z++ )
1235 {
1236 v3f clipdir = { 0.0f, 0.0f, 0.0f };
1237
1238 struct grid_sample *sa, *sb;
1239 if( i == 1 )
1240 {
1241 sa = &wg.samples[z][x];
1242 sb = &wg.samples[z+1][x];
1243 }
1244 else
1245 {
1246 sa = &wg.samples[x][z];
1247 sb = &wg.samples[x][z+1];
1248 }
1249
1250 if( sa->valid != sb->valid )
1251 {
1252 clipdir[i*2] = (float)(sa->valid - sb->valid) * k_gridscale;
1253 player_walkgrid_clip( wg.geo, tri_count,
1254 sa->valid? sa->pos: sb->pos,
1255 clipdir, sa->clip[i] );
1256 }
1257 else
1258 {
1259 if( sa->valid )
1260 {
1261 vg_line( sa->pos, sb->pos, 0xffffffff );
1262 }
1263 }
1264 }
1265 }
1266 }
1267
1268 /* Draw connections */
1269 struct grid_sample *corners[4];
1270 for( int x=0; x<WALKGRID_SIZE-1; x++ )
1271 {
1272 for( int z=0; z<WALKGRID_SIZE-1; z++ )
1273 {
1274 const struct conf *conf =
1275 player_walkgrid_conf( &wg, (v2i){x,z}, corners );
1276
1277 for( int i=0; i<conf->edge_count; i++ )
1278 {
1279 const struct confedge *edge = &conf->edges[i];
1280
1281 v3f p0, p1;
1282 v3_muladds( corners[edge->i0]->pos,
1283 corners[edge->d0]->clip[edge->a0], k_gridscale, p0 );
1284 v3_muladds( corners[edge->i1]->pos,
1285 corners[edge->d1]->clip[edge->a1], k_gridscale, p1 );
1286 vg_line( p0, p1, 0xff0000ff );
1287
1288 vg_line( corners[edge->i0]->pos, p0, 0xffffffff );
1289 vg_line( corners[edge->i1]->pos, p1, 0xffffffff );
1290 }
1291 }
1292 }
1293
1294 /*
1295 * Commit player movement into the grid
1296 */
1297
1298 v3f delta = {0.0f,0.0f,0.0f};
1299 v3f fwd = { -sinf(-player.angles[0]), 0.0f, -cosf(-player.angles[0]) },
1300 side = { -fwd[2], 0.0f, fwd[0] };
1301
1302 /* Temp */
1303 if( !vg_console_enabled() )
1304 {
1305 if( glfwGetKey( vg_window, GLFW_KEY_W ) )
1306 v3_muladds( delta, fwd, ktimestep*k_walkspeed, delta );
1307 if( glfwGetKey( vg_window, GLFW_KEY_S ) )
1308 v3_muladds( delta, fwd, -ktimestep*k_walkspeed, delta );
1309
1310 if( glfwGetKey( vg_window, GLFW_KEY_A ) )
1311 v3_muladds( delta, side, -ktimestep*k_walkspeed, delta );
1312 if( glfwGetKey( vg_window, GLFW_KEY_D ) )
1313 v3_muladds( delta, side, ktimestep*k_walkspeed, delta );
1314 }
1315
1316 if( v3_length2(delta) <= 0.00001f )
1317 return;
1318
1319 /*
1320 * Create our move in grid space
1321 */
1322 wg.dir[0] = delta[0] * (1.0f/k_gridscale);
1323 wg.dir[1] = delta[2] * (1.0f/k_gridscale);
1324 wg.move = 1.0f;
1325
1326 v2f region_pos =
1327 {
1328 (player.co[0] - wg.region[0][0]) * (1.0f/k_gridscale),
1329 (player.co[2] - wg.region[0][2]) * (1.0f/k_gridscale)
1330 };
1331 v2f region_cell_pos;
1332 v2_floor( region_pos, region_cell_pos );
1333 v2_sub( region_pos, region_cell_pos, wg.pos );
1334
1335 wg.cell_id[0] = region_cell_pos[0];
1336 wg.cell_id[1] = region_cell_pos[1];
1337
1338 int i=0;
1339 for(; i<8 && wg.move > 0.001f; i++ )
1340 player_walkgrid_iter( &wg, i );
1341
1342 player_walkgrid_stand_cell( &wg );
1343 }
1344
1345 static void player_walkgrid(void)
1346 {
1347 player_walkgrid_getsurface();
1348
1349 m4x3_mulv( player.to_world, (v3f){0.0f,1.8f,0.0f}, player.camera_pos );
1350 player_mouseview();
1351 player_transform_update();
1352 }
1353
1354 static void player_animate(void)
1355 {
1356 /* Camera position */
1357 v3_sub( player.v, player.v_last, player.a );
1358 v3_copy( player.v, player.v_last );
1359
1360 v3_add( player.m, player.a, player.m );
1361 v3_lerp( player.m, (v3f){0.0f,0.0f,0.0f}, 0.1f, player.m );
1362 v3f target;
1363
1364 player.m[0] = vg_clampf( player.m[0], -2.0f, 2.0f );
1365 player.m[1] = vg_clampf( player.m[1], -0.2f, 5.0f );
1366 player.m[2] = vg_clampf( player.m[2], -2.0f, 2.0f );
1367 v3_copy( player.m, target );
1368 v3_lerp( player.bob, target, 0.2f, player.bob );
1369
1370 /* Head */
1371 float lslip = fabsf(player.slip); //vg_minf( 0.4f, slip );
1372
1373 float grabt = vg_get_axis( "grabr" )*0.5f+0.5f;
1374 player.grab = vg_lerpf( player.grab, grabt, 0.04f );
1375
1376 float kheight = 2.0f,
1377 kleg = 0.6f;
1378
1379 v3f head;
1380 head[0] = 0.0f;
1381 head[1] = (0.3f+cosf(lslip)*0.5f*(1.0f-player.grab*0.7f)) * kheight;
1382 head[2] = 0.0f;
1383
1384 v3f offset;
1385 m3x3_mulv( player.to_local, player.bob, offset );
1386
1387 offset[0] *= 0.3333f;
1388 offset[1] *= -0.25f;
1389 offset[2] *= 0.7f;
1390 v3_muladds( head, offset, 0.7f, head );
1391 head[1] = vg_clampf( head[1], 0.3f, kheight );
1392
1393 #if 0
1394 if( !freecam )
1395 {
1396 v3_copy( head, player.view );
1397 v3f camoffs = {-0.2f,-0.6f,0.00f};
1398 v3_add( player.view, camoffs, player.view );
1399 }
1400 #endif
1401
1402 /*
1403 * Animation blending
1404 * ===========================================
1405 */
1406
1407 static float fslide = 0.0f;
1408 static float fdirz = 0.0f;
1409 static float fdirx = 0.0f;
1410 static float fstand = 0.0f;
1411 static float ffly = 0.0f;
1412
1413 float speed = v3_length( player.v );
1414
1415 fstand = vg_lerpf(fstand, 1.0f-vg_clampf(speed*0.03f,0.0f,1.0f),0.1f);
1416 fslide = vg_lerpf(fslide, vg_clampf(lslip+fabsf(offset[0])*0.2f,
1417 0.0f,1.0f), 0.04f);
1418 fdirz = vg_lerpf(fdirz, player.reverse > 0.0f? 1.0f: 0.0f, 0.04f );
1419 fdirx = vg_lerpf(fdirx, player.slip < 0.0f? 1.0f: 0.0f, 0.04f );
1420 ffly = vg_lerpf(ffly, player.in_air? 1.0f: 0.0f, 0.04f );
1421
1422 character_pose_reset( &player.mdl );
1423
1424 float amt_air = ffly*ffly,
1425 amt_ground = 1.0f-amt_air,
1426 amt_std = (1.0f-fslide) * amt_ground,
1427 amt_stand = amt_std * fstand,
1428 amt_aero = amt_std * (1.0f-fstand),
1429 amt_slide = amt_ground * fslide;
1430
1431 character_final_pose( &player.mdl, offset, &pose_stand, amt_stand );
1432 character_final_pose( &player.mdl, offset, &pose_aero, amt_aero*fdirz );
1433 character_final_pose( &player.mdl, offset,
1434 &pose_aero_reverse, amt_aero * (1.0f-fdirz) );
1435 character_final_pose( &player.mdl, offset, &pose_slide, amt_slide*fdirx );
1436 character_final_pose( &player.mdl, offset,
1437 &pose_slide1, amt_slide*(1.0f-fdirx) );
1438
1439 character_final_pose( &player.mdl, (v3f){0.0f,0.0f,0.0f},
1440 &pose_fly, amt_air );
1441
1442 /* Camera position */
1443 v3_lerp( player.smooth_localcam, player.mdl.cam_pos, 0.08f,
1444 player.smooth_localcam );
1445 v3_muladds( player.smooth_localcam, offset, 0.7f, player.camera_pos );
1446 player.camera_pos[1] = vg_clampf( player.camera_pos[1], 0.3f, kheight );
1447 m4x3_mulv( player.to_world, player.camera_pos, player.camera_pos );
1448
1449 /*
1450 * Additive effects
1451 * ==========================
1452 */
1453 struct ik_basic *arm_l = &player.mdl.ik_arm_l,
1454 *arm_r = &player.mdl.ik_arm_r;
1455
1456 v3f localv;
1457 m3x3_mulv( player.to_local, player.v, localv );
1458 v3_muladds( arm_l->end, localv, -0.01f, arm_l->end );
1459 v3_muladds( arm_r->end, localv, -0.01f, arm_r->end );
1460
1461 /* New board transformation */
1462 v4f board_rotation; v3f board_location;
1463
1464 v4f rz, rx;
1465 q_axis_angle( rz, (v3f){ 0.0f, 0.0f, 1.0f }, player.board_xy[0] );
1466 q_axis_angle( rx, (v3f){ 1.0f, 0.0f, 0.0f }, player.board_xy[1] );
1467 q_mul( rx, rz, board_rotation );
1468
1469 v3f *mboard = player.mdl.matrices[k_chpart_board];// player.mboard;
1470 q_m3x3( board_rotation, mboard );
1471 m3x3_mulv( mboard, (v3f){ 0.0f, -0.5f, 0.0f }, board_location );
1472 v3_add( (v3f){0.0f,0.5f,0.0f}, board_location, board_location );
1473 v3_copy( board_location, mboard[3] );
1474
1475
1476 float wheel_r = offset[0]*-0.4f;
1477 v4f qwheel;
1478 q_axis_angle( qwheel, (v3f){0.0f,1.0f,0.0f}, wheel_r );
1479
1480 q_m3x3( qwheel, player.mdl.matrices[k_chpart_wb] );
1481
1482 m3x3_transpose( player.mdl.matrices[k_chpart_wb],
1483 player.mdl.matrices[k_chpart_wf] );
1484 v3_copy( player.mdl.offsets[k_chpart_wb],
1485 player.mdl.matrices[k_chpart_wb][3] );
1486 v3_copy( player.mdl.offsets[k_chpart_wf],
1487 player.mdl.matrices[k_chpart_wf][3] );
1488
1489 m4x3_mul( mboard, player.mdl.matrices[k_chpart_wb],
1490 player.mdl.matrices[k_chpart_wb] );
1491 m4x3_mul( mboard, player.mdl.matrices[k_chpart_wf],
1492 player.mdl.matrices[k_chpart_wf] );
1493
1494 m4x3_mulv( mboard, player.mdl.ik_leg_l.end, player.mdl.ik_leg_l.end );
1495 m4x3_mulv( mboard, player.mdl.ik_leg_r.end, player.mdl.ik_leg_r.end );
1496
1497
1498 v3_copy( player.mdl.ik_arm_l.end, player.handl_target );
1499 v3_copy( player.mdl.ik_arm_r.end, player.handr_target );
1500
1501 if( 1||player.in_air )
1502 {
1503 float tuck = player.board_xy[1],
1504 tuck_amt = fabsf( tuck ) * (1.0f-fabsf(player.board_xy[0]));
1505
1506 float crouch = player.grab*0.3f;
1507 v3_muladds( player.mdl.ik_body.base, (v3f){0.0f,-1.0f,0.0f},
1508 crouch, player.mdl.ik_body.base );
1509 v3_muladds( player.mdl.ik_body.end, (v3f){0.0f,-1.0f,0.0f},
1510 crouch*1.2f, player.mdl.ik_body.end );
1511
1512 if( tuck < 0.0f )
1513 {
1514 //foot_l *= 1.0f-tuck_amt*1.5f;
1515
1516 if( player.grab > 0.1f )
1517 {
1518 m4x3_mulv( mboard, (v3f){0.1f,0.14f,0.6f},
1519 player.handl_target );
1520 }
1521 }
1522 else
1523 {
1524 //foot_r *= 1.0f-tuck_amt*1.4f;
1525
1526 if( player.grab > 0.1f )
1527 {
1528 m4x3_mulv( mboard, (v3f){0.1f,0.14f,-0.6f},
1529 player.handr_target );
1530 }
1531 }
1532 }
1533
1534 v3_lerp( player.handl, player.handl_target, 0.1f, player.handl );
1535 v3_lerp( player.handr, player.handr_target, 0.1f, player.handr );
1536
1537 v3_copy( player.handl, player.mdl.ik_arm_l.end );
1538 v3_copy( player.handr, player.mdl.ik_arm_r.end );
1539
1540 /* Head rotation */
1541
1542 static float rhead = 0.0f;
1543 rhead = vg_lerpf( rhead,
1544 vg_clampf(atan2f( localv[2], -localv[0] ),-1.0f,1.0f), 0.04f );
1545 player.mdl.rhead = rhead;
1546 }
1547
1548 static void player_update(void)
1549 {
1550 if( vg_get_axis("grabl")>0.0f)
1551 reset_player(0,NULL);
1552
1553 if( freecam )
1554 {
1555 player_freecam();
1556 }
1557 else
1558 {
1559 if( player.is_dead )
1560 {
1561 character_ragdoll_iter( &player.mdl );
1562 character_debug_ragdoll( &player.mdl );
1563 }
1564 else
1565 {
1566 if( player.on_board )
1567 {
1568 player_do_motion();
1569 player_animate();
1570 }
1571 else
1572 {
1573 player_walkgrid();
1574 }
1575 }
1576 }
1577
1578 /* Update camera matrices */
1579 m4x3_identity( player.camera );
1580 m4x3_rotate_y( player.camera, -player.angles[0] );
1581 m4x3_rotate_x( player.camera, -0.33f -player.angles[1] );
1582 v3_copy( player.camera_pos, player.camera[3] );
1583 m4x3_invert_affine( player.camera, player.camera_inverse );
1584 }
1585
1586 static void draw_player(void)
1587 {
1588 /* Draw */
1589 m4x3_copy( player.to_world, player.mdl.mroot );
1590
1591 if( player.is_dead )
1592 character_mimic_ragdoll( &player.mdl );
1593 else
1594 character_eval( &player.mdl );
1595
1596 character_draw( &player.mdl, (player.is_dead|player.in_air)? 0.0f: 1.0f );
1597 }
1598
1599 #endif /* PLAYER_H */