stuff
[carveJwlIkooP6JGAAIwe30JlM.git] / player.h
1 #ifndef PLAYER_H
2 #define PLAYER_H
3
4 #include "audio.h"
5 #include "common.h"
6 #include "world.h"
7 //#include "character.h"
8 #include "player_model.h"
9 #include "bvh.h"
10
11 /*
12 * Constants
13 */
14
15 static float
16 k_walkspeed = 2.0f,
17 k_board_radius = 0.3f,
18 k_board_length = 0.45f,
19 k_board_allowance = 0.04f,
20 k_friction_lat = 8.8f,
21 k_friction_resistance = 0.01f,
22 k_max_push_speed = 16.0f,
23 k_push_accel = 5.0f,
24 k_push_cycle_rate = 8.0f,
25 k_steer_ground = 2.5f,
26 k_steer_air = 3.6f,
27 k_steer_air_lerp = 0.3f,
28 k_pump_force = 000.0f,
29 k_downforce = 5.0f;
30
31 static int freecam = 0;
32 static int walk_grid_iterations = 1;
33 static float fc_speed = 10.0f;
34
35 static struct gplayer
36 {
37 /* Physics */
38 rigidbody rb, collide_front, collide_back, rb_gate_frame;
39
40 v3f a, v_last, m, bob, vl;
41
42 /* Utility */
43 float vswitch, slip, slip_last,
44 reverse;
45
46 float iY; /* Yaw inertia */
47 int in_air, is_dead, on_board;
48
49 v2f board_xy;
50 float grab;
51 float pitch;
52 float pushing, push_time;
53
54 v3f land_target;
55 v3f land_target_log[22];
56 u32 land_target_colours[22];
57 int land_log_count;
58 m3x3f vr,vr_pstep;
59
60 struct character mdl;
61
62 v3f handl_target, handr_target,
63 handl, handr;
64
65 /* Camera */
66 float air_blend;
67
68 v3f camera_pos, smooth_localcam;
69 v2f angles;
70 m4x3f camera, camera_inverse;
71 }
72 player =
73 {
74 .on_board = 1,
75
76 .collide_front = { .type = k_rb_shape_sphere, .inf.sphere.radius = 0.3f },
77 .collide_back = { .type = k_rb_shape_sphere, .inf.sphere.radius = 0.3f }
78 };
79
80 /*
81 * Player API
82 */
83
84
85 /*
86 * Free camera movement
87 */
88
89 static void player_mouseview(void)
90 {
91 if( gui_want_mouse() )
92 return;
93
94 static v2f mouse_last,
95 view_vel = { 0.0f, 0.0f };
96
97 if( vg_get_button_down( "primary" ) )
98 v2_copy( vg_mouse, mouse_last );
99
100 else if( vg_get_button( "primary" ) )
101 {
102 v2f delta;
103 v2_sub( vg_mouse, mouse_last, delta );
104 v2_copy( vg_mouse, mouse_last );
105
106 v2_muladds( view_vel, delta, 0.001f, view_vel );
107 }
108
109 v2_muladds( view_vel,
110 (v2f){ vg_get_axis("h1"), vg_get_axis("v1") },
111 0.05f, view_vel );
112 v2_muls( view_vel, 0.93f, view_vel );
113 v2_add( view_vel, player.angles, player.angles );
114 player.angles[1] = vg_clampf( player.angles[1], -VG_PIf*0.5f, VG_PIf*0.5f );
115 }
116
117 static void player_freecam(void)
118 {
119 player_mouseview();
120
121 float movespeed = fc_speed;
122 v3f lookdir = { 0.0f, 0.0f, -1.0f },
123 sidedir = { 1.0f, 0.0f, 0.0f };
124
125 m3x3_mulv( player.camera, lookdir, lookdir );
126 m3x3_mulv( player.camera, sidedir, sidedir );
127
128 static v3f move_vel = { 0.0f, 0.0f, 0.0f };
129 if( vg_get_button( "forward" ) )
130 v3_muladds( move_vel, lookdir, ktimestep * movespeed, move_vel );
131 if( vg_get_button( "back" ) )
132 v3_muladds( move_vel, lookdir, ktimestep *-movespeed, move_vel );
133 if( vg_get_button( "left" ) )
134 v3_muladds( move_vel, sidedir, ktimestep *-movespeed, move_vel );
135 if( vg_get_button( "right" ) )
136 v3_muladds( move_vel, sidedir, ktimestep * movespeed, move_vel );
137
138 v3_muls( move_vel, 0.7f, move_vel );
139 v3_add( move_vel, player.camera_pos, player.camera_pos );
140 }
141
142 /*
143 * Player Physics Implementation
144 */
145
146 static void apply_gravity( v3f vel, float const timestep )
147 {
148 v3f gravity = { 0.0f, -9.6f, 0.0f };
149 v3_muladds( vel, gravity, timestep, vel );
150 }
151
152 /*
153 * TODO: The angle bias should become greater when launching from a steeper
154 * angle and skewed towords more 'downwards' angles when launching from
155 * shallower trajectories
156 *
157 * it should also be tweaked by the controller left stick being pushed
158 * up or down
159 */
160 static void player_start_air(void)
161 {
162 if( player.in_air )
163 return;
164
165 player.in_air = 1;
166
167 float pstep = ktimestep*10.0f;
168 float best_velocity_delta = -9999.9f;
169 float k_bias = 0.96f;
170
171 v3f axis;
172 v3_cross( player.rb.up, player.rb.v, axis );
173 v3_normalize( axis );
174 player.land_log_count = 0;
175
176 m3x3_identity( player.vr );
177
178 for( int m=-3;m<=12; m++ )
179 {
180 float vmod = ((float)m / 15.0f)*0.09f;
181
182 v3f pco, pco1, pv;
183 v3_copy( player.rb.co, pco );
184 v3_muls( player.rb.v, k_bias, pv );
185
186 /*
187 * Try different 'rotations' of the velocity to find the best possible
188 * landing normal. This conserves magnitude at the expense of slightly
189 * unrealistic results
190 */
191
192 m3x3f vr;
193 v4f vr_q;
194
195 q_axis_angle( vr_q, axis, vmod );
196 q_m3x3( vr_q, vr );
197
198 m3x3_mulv( vr, pv, pv );
199 v3_muladds( pco, pv, pstep, pco );
200
201 for( int i=0; i<50; i++ )
202 {
203 v3_copy( pco, pco1 );
204 apply_gravity( pv, pstep );
205
206 m3x3_mulv( vr, pv, pv );
207 v3_muladds( pco, pv, pstep, pco );
208
209 ray_hit contact;
210 v3f vdir;
211
212 v3_sub( pco, pco1, vdir );
213 contact.dist = v3_length( vdir );
214 v3_divs( vdir, contact.dist, vdir);
215
216 if( ray_world( pco1, vdir, &contact ))
217 {
218 float land_delta = v3_dot( pv, contact.normal );
219 u32 scolour = (u8)(vg_minf(-land_delta * 2.0f, 255.0f));
220
221 /* Bias prediction towords ramps */
222 if( ray_hit_is_ramp( &contact ) )
223 {
224 land_delta *= 0.1f;
225 scolour |= 0x0000a000;
226 }
227
228 if( (land_delta < 0.0f) && (land_delta > best_velocity_delta) )
229 {
230 best_velocity_delta = land_delta;
231
232 v3_copy( contact.pos, player.land_target );
233
234 m3x3_copy( vr, player.vr_pstep );
235 q_axis_angle( vr_q, axis, vmod*0.1f );
236 q_m3x3( vr_q, player.vr );
237 }
238
239 v3_copy( contact.pos,
240 player.land_target_log[player.land_log_count] );
241 player.land_target_colours[player.land_log_count] =
242 0xff000000 | scolour;
243
244 player.land_log_count ++;
245
246 break;
247 }
248 }
249 }
250 }
251
252 static void draw_cross(v3f pos,u32 colour, float scale)
253 {
254 v3f p0, p1;
255 v3_add( (v3f){ scale,0.0f,0.0f}, pos, p0 );
256 v3_add( (v3f){-scale,0.0f,0.0f}, pos, p1 );
257 vg_line( p0, p1, colour );
258 v3_add( (v3f){0.0f, scale,0.0f}, pos, p0 );
259 v3_add( (v3f){0.0f,-scale,0.0f}, pos, p1 );
260 vg_line( p0, p1, colour );
261 v3_add( (v3f){0.0f,0.0f, scale}, pos, p0 );
262 v3_add( (v3f){0.0f,0.0f,-scale}, pos, p1 );
263 vg_line( p0, p1, colour );
264 }
265
266 static void player_physics_control(void)
267 {
268 /*
269 * Computing localized friction forces for controlling the character
270 * Friction across X is significantly more than Z
271 */
272
273 v3f vel;
274 m3x3_mulv( player.rb.to_local, player.rb.v, vel );
275 float slip = 0.0f;
276
277 if( fabsf(vel[2]) > 0.01f )
278 slip = fabsf(-vel[0] / vel[2]) * vg_signf(vel[0]);
279
280 if( fabsf( slip ) > 1.2f )
281 slip = vg_signf( slip ) * 1.2f;
282 player.slip = slip;
283 player.reverse = -vg_signf(vel[2]);
284
285 float substep = ktimestep * 0.2f;
286 float fwd_resistance = (vg_get_button( "break" )? 5.0f: 0.02f) * -substep;
287
288 for( int i=0; i<5; i++ )
289 {
290 vel[2] = stable_force( vel[2], vg_signf( vel[2] ) * fwd_resistance );
291 vel[0] = stable_force( vel[0],
292 vg_signf( vel[0] ) * -k_friction_lat*substep );
293 }
294
295 static double start_push = 0.0;
296 if( vg_get_button_down( "push" ) )
297 start_push = vg_time;
298
299 if( !vg_get_button("break") && vg_get_button( "push" ) )
300 {
301 player.pushing = 1.0f;
302 player.push_time = vg_time-start_push;
303
304 float cycle_time = player.push_time*k_push_cycle_rate,
305 amt = k_push_accel * (sinf(cycle_time)*0.5f+0.5f)*ktimestep,
306 current = v3_length( vel ),
307 new_vel = vg_minf( current + amt, k_max_push_speed );
308
309 new_vel -= vg_minf(current, k_max_push_speed);
310 vel[2] -= new_vel * player.reverse;
311 }
312
313 /* Pumping */
314 static float previous = 0.0f;
315 float delta = previous - player.grab,
316 pump = delta * k_pump_force*ktimestep;
317 previous = player.grab;
318
319 v3f p1;
320 v3_muladds( player.rb.co, player.rb.up, pump, p1 );
321 vg_line( player.rb.co, p1, 0xff0000ff );
322
323 vel[1] += pump;
324
325
326 m3x3_mulv( player.rb.to_world, vel, player.rb.v );
327
328 float steer = vg_get_axis( "horizontal" );
329 player.iY -= vg_signf(steer)*powf(steer,2.0f) * k_steer_ground * ktimestep;
330
331 v2_lerp( player.board_xy, (v2f){ slip*0.25f, 0.0f },
332 ktimestep*5.0f, player.board_xy);
333 }
334
335 static void player_physics_control_air(void)
336 {
337 m3x3_mulv( player.vr, player.rb.v, player.rb.v );
338 draw_cross( player.land_target, 0xff0000ff, 0.25f );
339
340 ray_hit hit;
341
342 /*
343 * Prediction
344 */
345 float pstep = ktimestep*10.0f;
346
347 v3f pco, pco1, pv;
348 v3_copy( player.rb.co, pco );
349 v3_copy( player.rb.v, pv );
350
351 float time_to_impact = 0.0f;
352 float limiter = 1.0f;
353
354 for( int i=0; i<50; i++ )
355 {
356 v3_copy( pco, pco1 );
357 m3x3_mulv( player.vr_pstep, pv, pv );
358 apply_gravity( pv, pstep );
359 v3_muladds( pco, pv, pstep, pco );
360
361 //vg_line( pco, pco1, i&0x1?0xff000000:0xffffffff );
362
363 ray_hit contact;
364 v3f vdir;
365
366 v3_sub( pco, pco1, vdir );
367 contact.dist = v3_length( vdir );
368 v3_divs( vdir, contact.dist, vdir);
369
370 float orig_dist = contact.dist;
371 if( ray_world( pco1, vdir, &contact ))
372 {
373 float angle = v3_dot( player.rb.up, contact.normal );
374 v3f axis;
375 v3_cross( player.rb.up, contact.normal, axis );
376
377 time_to_impact += (contact.dist/orig_dist)*pstep;
378 limiter = vg_minf( 5.0f, time_to_impact )/5.0f;
379 limiter = 1.0f-limiter;
380 limiter *= limiter;
381 limiter = 1.0f-limiter;
382
383 if( angle < 0.99f )
384 {
385 v4f correction;
386 q_axis_angle( correction, axis, acosf(angle)*0.05f*(1.0f-limiter) );
387 q_mul( correction, player.rb.q, player.rb.q );
388 }
389
390 draw_cross( contact.pos, 0xffff0000, 0.25f );
391 break;
392 }
393 time_to_impact += pstep;
394 }
395
396 player.iY -= vg_get_axis( "horizontal" ) * k_steer_air * ktimestep;
397 {
398 float iX = vg_get_axis( "vertical" ) *
399 player.reverse * k_steer_air * limiter * ktimestep;
400
401 static float siX = 0.0f;
402 siX = vg_lerpf( siX, iX, k_steer_air_lerp );
403
404 v4f rotate;
405 q_axis_angle( rotate, player.rb.right, siX );
406 q_mul( rotate, player.rb.q, player.rb.q );
407 }
408
409 v2f target = {0.0f,0.0f};
410 v2_muladds( target, (v2f){ vg_get_axis("h1"), vg_get_axis("v1") },
411 player.grab, target );
412 v2_lerp( player.board_xy, target, ktimestep*3.0f, player.board_xy );
413 }
414
415 static void player_init(void)
416 {
417 rb_init( &player.collide_front );
418 rb_init( &player.collide_back );
419 }
420
421 static void player_physics(void)
422 {
423 /*
424 * Update collision fronts
425 */
426
427 rigidbody *rbf = &player.collide_front,
428 *rbb = &player.collide_back;
429
430 m3x3_copy( player.rb.to_world, player.collide_front.to_world );
431 m3x3_copy( player.rb.to_world, player.collide_back.to_world );
432
433 player.air_blend = vg_lerpf( player.air_blend, player.in_air, 0.1f );
434 float h = player.air_blend*0.2f;
435
436 m4x3_mulv( player.rb.to_world, (v3f){0.0f,h,-k_board_length}, rbf->co );
437 v3_copy( rbf->co, rbf->to_world[3] );
438 m4x3_mulv( player.rb.to_world, (v3f){0.0f,h, k_board_length}, rbb->co );
439 v3_copy( rbb->co, rbb->to_world[3] );
440
441 m4x3_invert_affine( rbf->to_world, rbf->to_local );
442 m4x3_invert_affine( rbb->to_world, rbb->to_local );
443
444 rb_update_bounds( rbf );
445 rb_update_bounds( rbb );
446
447 rb_debug( rbf, 0xff00ffff );
448 rb_debug( rbb, 0xffffff00 );
449
450 rb_ct manifold[24];
451 int len = 0;
452
453 len += rb_sphere_vs_scene( rbf, &world.rb_geo, manifold+len );
454 len += rb_sphere_vs_scene( rbb, &world.rb_geo, manifold+len );
455
456 rb_presolve_contacts( manifold, len );
457 v3f surface_avg = {0.0f, 0.0f, 0.0f};
458
459 if( !len )
460 {
461 player_start_air();
462 }
463 else
464 {
465 for( int i=0; i<len; i++ )
466 {
467 v3_add( manifold[i].n, surface_avg, surface_avg );
468
469 #if 0
470 if( manifold[i].element_id <= world.sm_geo_std_oob.vertex_count )
471 {
472 player.is_dead = 1;
473 character_ragdoll_copypose( &player.mdl, player.rb.v );
474 return;
475 }
476 #endif
477 }
478
479 v3_normalize( surface_avg );
480
481 if( v3_dot( player.rb.v, surface_avg ) > 0.5f )
482 {
483 player_start_air();
484 }
485 else
486 player.in_air = 0;
487 }
488
489 for( int j=0; j<5; j++ )
490 {
491 for( int i=0; i<len; i++ )
492 {
493 struct contact *ct = &manifold[i];
494
495 v3f dv, delta;
496 v3_sub( ct->co, player.rb.co, delta );
497 v3_cross( player.rb.w, delta, dv );
498 v3_add( player.rb.v, dv, dv );
499
500 float vn = -v3_dot( dv, ct->n );
501 vn += ct->bias;
502
503 float temp = ct->norm_impulse;
504 ct->norm_impulse = vg_maxf( temp + vn, 0.0f );
505 vn = ct->norm_impulse - temp;
506
507 v3f impulse;
508 v3_muls( ct->n, vn, impulse );
509
510 if( fabsf(v3_dot( impulse, player.rb.forward )) > 10.0f ||
511 fabsf(v3_dot( impulse, player.rb.up )) > 50.0f )
512 {
513 player.is_dead = 1;
514 character_ragdoll_copypose( &player.mdl, player.rb.v );
515 return;
516 }
517
518 v3_add( impulse, player.rb.v, player.rb.v );
519 v3_cross( delta, impulse, impulse );
520
521 /*
522 * W Impulses are limited to the Y and X axises, we don't really want
523 * roll angular velocities being included.
524 *
525 * Can also tweak the resistance of each axis here by scaling the wx,wy
526 * components.
527 */
528
529 float wy = v3_dot( player.rb.up, impulse ),
530 wx = v3_dot( player.rb.right, impulse )*1.5f;
531
532 v3_muladds( player.rb.w, player.rb.up, wy, player.rb.w );
533 v3_muladds( player.rb.w, player.rb.right, wx, player.rb.w );
534 }
535 }
536
537 float grabt = vg_get_axis( "grabr" )*0.5f+0.5f;
538 player.grab = vg_lerpf( player.grab, grabt, 0.14f );
539 player.pushing = 0.0f;
540
541 if( !player.in_air )
542 {
543 v3f axis;
544 float angle = v3_dot( player.rb.up, surface_avg );
545 v3_cross( player.rb.up, surface_avg, axis );
546
547 //float cz = v3_dot( player.rb.forward, axis );
548 //v3_muls( player.rb.forward, cz, axis );
549
550 if( angle < 0.999f )
551 {
552 v4f correction;
553 q_axis_angle( correction, axis, acosf(angle)*0.3f );
554 q_mul( correction, player.rb.q, player.rb.q );
555 }
556
557 v3_muladds( player.rb.v, player.rb.up,
558 -k_downforce*ktimestep, player.rb.v );
559 player_physics_control();
560 }
561 else
562 {
563 player_physics_control_air();
564 }
565 }
566
567 static void player_do_motion(void)
568 {
569 float horizontal = vg_get_axis("horizontal"),
570 vertical = vg_get_axis("vertical");
571
572 player_physics();
573
574 /* Integrate velocity */
575 v3f prevco;
576 v3_copy( player.rb.co, prevco );
577
578 apply_gravity( player.rb.v, ktimestep );
579 v3_muladds( player.rb.co, player.rb.v, ktimestep, player.rb.co );
580
581 /* Real angular velocity integration */
582 v3_lerp( player.rb.w, (v3f){0.0f,0.0f,0.0f}, 0.125f, player.rb.w );
583 if( v3_length2( player.rb.w ) > 0.0f )
584 {
585 v4f rotation;
586 v3f axis;
587 v3_copy( player.rb.w, axis );
588
589 float mag = v3_length( axis );
590 v3_divs( axis, mag, axis );
591 q_axis_angle( rotation, axis, mag*k_rb_delta );
592 q_mul( rotation, player.rb.q, player.rb.q );
593 }
594
595 /* Faux angular velocity */
596 v4f rotate;
597
598 static float siY = 0.0f;
599 float lerpq = player.in_air? 0.04f: 0.3f;
600 siY = vg_lerpf( siY, player.iY, lerpq );
601
602 q_axis_angle( rotate, player.rb.up, siY );
603 q_mul( rotate, player.rb.q, player.rb.q );
604 player.iY = 0.0f;
605
606 /*
607 * Gate intersection, by tracing a line over the gate planes
608 */
609 for( int i=0; i<world.routes.gate_count; i++ )
610 {
611 struct route_gate *rg = &world.routes.gates[i];
612 teleport_gate *gate = &rg->gate;
613
614 if( gate_intersect( gate, player.rb.co, prevco ) )
615 {
616 m4x3_mulv( gate->transport, player.rb.co, player.rb.co );
617 m3x3_mulv( gate->transport, player.rb.v, player.rb.v );
618 m3x3_mulv( gate->transport, player.vl, player.vl );
619 m3x3_mulv( gate->transport, player.v_last, player.v_last );
620 m3x3_mulv( gate->transport, player.m, player.m );
621 m3x3_mulv( gate->transport, player.bob, player.bob );
622
623 v4f transport_rotation;
624 m3x3_q( gate->transport, transport_rotation );
625 q_mul( transport_rotation, player.rb.q, player.rb.q );
626
627 world_routes_activate_gate( i );
628 player.rb_gate_frame = player.rb;
629 break;
630 }
631 }
632
633 rb_update_transform( &player.rb );
634 }
635
636 /*
637 * Walkgrid implementation,
638 * loosely based of cmuratoris youtube video 'Killing the Walkmonster'
639 */
640
641 #define WALKGRID_SIZE 16
642 struct walkgrid
643 {
644 struct grid_sample
645 {
646 enum sample_type
647 {
648 k_sample_type_air, /* Nothing was hit. */
649 k_sample_type_invalid, /* The point is invalid, but there is a sample
650 underneath that can be used */
651 k_sample_type_valid, /* This point is good */
652 }
653 type;
654
655 v3f clip[2];
656 v3f pos;
657
658 enum traverse_state
659 {
660 k_traverse_none = 0x00,
661 k_traverse_h = 0x01,
662 k_traverse_v = 0x02
663 }
664 state;
665 }
666 samples[WALKGRID_SIZE][WALKGRID_SIZE];
667
668 boxf region;
669
670 float move; /* Current amount of movement we have left to apply */
671 v2f dir; /* The movement delta */
672 v2i cell_id;/* Current cell */
673 v2f pos; /* Local position (in cell) */
674 float h;
675 };
676
677 static int player_walkgrid_tri_walkable( u32 tri[3] )
678 {
679 return tri[0] > world.sm_geo_std_oob.vertex_count;
680 }
681
682 /*
683 * Get a sample at this pole location, will return 1 if the sample is valid,
684 * and pos will be updated to be the intersection location.
685 */
686 static void player_walkgrid_samplepole( struct grid_sample *s )
687 {
688 boxf region = {{ s->pos[0] -0.01f, s->pos[1] - 4.0f, s->pos[2] -0.01f},
689 { s->pos[0] +0.01f, s->pos[1] + 4.0f, s->pos[2] +0.01f}};
690
691 u32 geo[256];
692 v3f tri[3];
693 int len = bh_select( &world.geo.bhtris, region, geo, 256 );
694
695 const float k_minworld_y = -2000.0f;
696
697 float walk_height = k_minworld_y,
698 block_height = k_minworld_y;
699
700 s->type = k_sample_type_air;
701
702 for( int i=0; i<len; i++ )
703 {
704 u32 *ptri = &world.geo.indices[ geo[i]*3 ];
705
706 for( int j=0; j<3; j++ )
707 v3_copy( world.geo.verts[ptri[j]].co, tri[j] );
708
709 v3f vdown = {0.0f,-1.0f,0.0f};
710 v3f sample_from;
711 v3_copy( s->pos, sample_from );
712 sample_from[1] = region[1][1];
713
714 float dist;
715 if( ray_tri( tri, sample_from, vdown, &dist ))
716 {
717 v3f p0;
718 v3_muladds( sample_from, vdown, dist, p0 );
719
720 if( player_walkgrid_tri_walkable(ptri) )
721 {
722 if( p0[1] > walk_height )
723 {
724 walk_height = p0[1];
725 }
726 }
727 else
728 {
729 if( p0[1] > block_height )
730 block_height = p0[1];
731 }
732 }
733 }
734
735 s->pos[1] = walk_height;
736
737 if( walk_height > k_minworld_y )
738 if( block_height > walk_height )
739 s->type = k_sample_type_invalid;
740 else
741 s->type = k_sample_type_valid;
742 else
743 s->type = k_sample_type_air;
744 }
745
746 float const k_gridscale = 0.5f;
747
748 enum eclipdir
749 {
750 k_eclipdir_h = 0,
751 k_eclipdir_v = 1
752 };
753
754 static void player_walkgrid_clip_blocker( struct grid_sample *sa,
755 struct grid_sample *sb,
756 struct grid_sample *st,
757 enum eclipdir dir )
758 {
759 v3f clipdir, pos;
760 int valid_a = sa->type == k_sample_type_valid,
761 valid_b = sb->type == k_sample_type_valid;
762 struct grid_sample *target = valid_a? sa: sb,
763 *other = valid_a? sb: sa;
764 v3_copy( target->pos, pos );
765 v3_sub( other->pos, target->pos, clipdir );
766
767 boxf cell_region;
768 v3_muladds( pos, (v3f){1.0f,1.0f,1.0f}, -k_gridscale*2.1f, cell_region[0]);
769 v3_muladds( pos, (v3f){1.0f,1.0f,1.0f}, k_gridscale*2.1f, cell_region[1]);
770
771 u32 geo[256];
772 v3f tri[3];
773 int len = bh_select( &world.geo.bhtris, cell_region, geo, 256 );
774
775 float start_time = v3_length( clipdir ),
776 min_time = start_time;
777 v3_normalize( clipdir );
778 v3_muls( clipdir, 0.0001f, st->clip[dir] );
779
780 for( int i=0; i<len; i++ )
781 {
782 u32 *ptri = &world.geo.indices[ geo[i]*3 ];
783 for( int j=0; j<3; j++ )
784 v3_copy( world.geo.verts[ptri[j]].co, tri[j] );
785
786 if( player_walkgrid_tri_walkable(ptri) )
787 continue;
788
789 float dist;
790 if(ray_tri( tri, pos, clipdir, &dist ))
791 {
792 if( dist > 0.0f && dist < min_time )
793 {
794 min_time = dist;
795 sb->type = k_sample_type_air;
796 }
797 }
798 }
799
800 if( !(min_time < start_time) )
801 min_time = 0.5f * k_gridscale;
802
803 min_time = vg_clampf( min_time/k_gridscale, 0.01f, 0.99f );
804
805 v3_muls( clipdir, min_time, st->clip[dir] );
806
807 v3f p0;
808 v3_muladds( target->pos, st->clip[dir], k_gridscale, p0 );
809 }
810
811 static void player_walkgrid_clip_edge( struct grid_sample *sa,
812 struct grid_sample *sb,
813 struct grid_sample *st, /* data store */
814 enum eclipdir dir )
815 {
816 v3f clipdir = { 0.0f, 0.0f, 0.0f }, pos;
817 int valid_a = sa->type == k_sample_type_valid,
818 valid_b = sb->type == k_sample_type_valid;
819
820 struct grid_sample *target = valid_a? sa: sb,
821 *other = valid_a? sb: sa;
822
823 v3_sub( other->pos, target->pos, clipdir );
824 clipdir[1] = 0.0f;
825
826 v3_copy( target->pos, pos );
827
828 boxf cell_region;
829 v3_muladds( pos, (v3f){1.0f,1.0f,1.0f}, -k_gridscale*1.1f, cell_region[0]);
830 v3_muladds( pos, (v3f){1.0f,1.0f,1.0f}, k_gridscale*1.1f, cell_region[1]);
831
832 u32 geo[256];
833 int len = bh_select( &world.geo.bhtris, cell_region, geo, 256 );
834
835 float max_dist = 0.0f;
836 v3f tri[3];
837 v3f perp;
838 v3_cross( clipdir,(v3f){0.0f,1.0f,0.0f},perp );
839 v3_muls( clipdir, 0.001f, st->clip[dir] );
840
841 for( int i=0; i<len; i++ )
842 {
843 u32 *ptri = &world.geo.indices[ geo[i]*3 ];
844 for( int j=0; j<3; j++ )
845 v3_copy( world.geo.verts[ptri[j]].co, tri[j] );
846
847 if( !player_walkgrid_tri_walkable(ptri) )
848 continue;
849
850 for( int k=0; k<3; k++ )
851 {
852 int ia = k,
853 ib = (k+1)%3;
854
855 v3f v0, v1;
856 v3_sub( tri[ia], pos, v0 );
857 v3_sub( tri[ib], pos, v1 );
858
859 if( (clipdir[2]*v0[0] - clipdir[0]*v0[2]) *
860 (clipdir[2]*v1[0] - clipdir[0]*v1[2]) < 0.0f )
861 {
862 float da = v3_dot(v0,perp),
863 db = v3_dot(v1,perp),
864 d = da-db,
865 qa = da/d;
866
867 v3f p0;
868 v3_muls( v1, qa, p0 );
869 v3_muladds( p0, v0, 1.0f-qa, p0 );
870
871 float h = v3_dot(p0,clipdir)/v3_dot(clipdir,clipdir);
872
873 if( h >= max_dist && h <= 1.0f )
874 {
875 max_dist = h;
876 float l = 1.0f/v3_length(clipdir);
877 v3_muls( p0, l, st->clip[dir] );
878 }
879 }
880 }
881 }
882 }
883
884 static const struct conf
885 {
886 struct confedge
887 {
888 /* i: sample index
889 * d: data index
890 * a: axis index
891 * o: the 'other' point to do a A/B test with
892 * if its -1, all AB is done.
893 */
894 int i0, i1,
895 d0, d1,
896 a0, a1,
897 o0, o1;
898 }
899 edges[2];
900 int edge_count;
901 }
902 k_walkgrid_configs[16] = {
903 {{},0},
904 {{{ 3,3, 3,0, 1,0, -1,-1 }}, 1},
905 {{{ 2,2, 1,3, 0,1, -1,-1 }}, 1},
906 {{{ 2,3, 1,0, 0,0, 3,-1 }}, 1},
907
908 {{{ 1,1, 0,1, 1,0, -1,-1 }}, 1},
909 {{{ 3,3, 3,0, 1,0, -1,-1 },
910 { 1,1, 0,1, 1,0, -1,-1 }}, 2},
911 {{{ 1,2, 0,3, 1,1, 2,-1 }}, 1},
912 {{{ 1,3, 0,0, 1,0, 2, 2 }}, 1},
913
914 {{{ 0,0, 0,0, 0,1, -1,-1 }}, 1},
915 {{{ 3,0, 3,0, 1,1, 0,-1 }}, 1},
916 {{{ 2,2, 1,3, 0,1, -1,-1 },
917 { 0,0, 0,0, 0,1, -1,-1 }}, 2},
918 {{{ 2,0, 1,0, 0,1, 3, 3 }}, 1},
919
920 {{{ 0,1, 0,1, 0,0, 1,-1 }}, 1},
921 {{{ 3,1, 3,1, 1,0, 0, 0 }}, 1},
922 {{{ 0,2, 0,3, 0,1, 1, 1 }}, 1},
923 {{},0},
924 };
925
926 /*
927 * Get a buffer of edges from cell location
928 */
929 static const struct conf *player_walkgrid_conf( struct walkgrid *wg,
930 v2i cell,
931 struct grid_sample *corners[4] )
932 {
933 corners[0] = &wg->samples[cell[1] ][cell[0] ];
934 corners[1] = &wg->samples[cell[1]+1][cell[0] ];
935 corners[2] = &wg->samples[cell[1]+1][cell[0]+1];
936 corners[3] = &wg->samples[cell[1] ][cell[0]+1];
937
938 u32 vd0 = corners[0]->type == k_sample_type_valid,
939 vd1 = corners[1]->type == k_sample_type_valid,
940 vd2 = corners[2]->type == k_sample_type_valid,
941 vd3 = corners[3]->type == k_sample_type_valid,
942 config = (vd0<<3) | (vd1<<2) | (vd2<<1) | vd3;
943
944 return &k_walkgrid_configs[ config ];
945 }
946
947 static void player_walkgrid_floor(v3f pos)
948 {
949 v3_muls( pos, 1.0f/k_gridscale, pos );
950 v3_floor( pos, pos );
951 v3_muls( pos, k_gridscale, pos );
952 }
953
954 /*
955 * Computes the barycentric coordinate of location on a triangle (vertical),
956 * then sets the Y position to the interpolation of the three points
957 */
958 static void player_walkgrid_stand_tri( v3f a, v3f b, v3f c, v3f pos )
959 {
960 v3f v0,v1,v2;
961 v3_sub( b, a, v0 );
962 v3_sub( c, a, v1 );
963 v3_sub( pos, a, v2 );
964
965 float d = v0[0]*v1[2] - v1[0]*v0[2],
966 v = (v2[0]*v1[2] - v1[0]*v2[2]) / d,
967 w = (v0[0]*v2[2] - v2[0]*v0[2]) / d,
968 u = 1.0f - v - w;
969
970 vg_line( pos, a, 0xffff0000 );
971 vg_line( pos, b, 0xff00ff00 );
972 vg_line( pos, c, 0xff0000ff );
973 pos[1] = u*a[1] + v*b[1] + w*c[1];
974 }
975
976 /*
977 * Get the minimum time value of pos+dir until a cell edge
978 *
979 * t[0] -> t[3] are the individual time values
980 * t[5] & t[6] are the maximum axis values
981 * t[6] is the minimum value
982 *
983 */
984 static void player_walkgrid_min_cell( float t[7], v2f pos, v2f dir )
985 {
986 v2f frac = { 1.0f/dir[0], 1.0f/dir[1] };
987
988 t[0] = 999.9f;
989 t[1] = 999.9f;
990 t[2] = 999.9f;
991 t[3] = 999.9f;
992
993 if( fabsf(dir[0]) > 0.0001f )
994 {
995 t[0] = (0.0f-pos[0]) * frac[0];
996 t[1] = (1.0f-pos[0]) * frac[0];
997 }
998 if( fabsf(dir[1]) > 0.0001f )
999 {
1000 t[2] = (0.0f-pos[1]) * frac[1];
1001 t[3] = (1.0f-pos[1]) * frac[1];
1002 }
1003
1004 t[4] = vg_maxf(t[0],t[1]);
1005 t[5] = vg_maxf(t[2],t[3]);
1006 t[6] = vg_minf(t[4],t[5]);
1007 }
1008
1009 static void player_walkgrid_iter(struct walkgrid *wg, int iter)
1010 {
1011
1012 /*
1013 * For each walkgrid iteration we are stepping through cells and determining
1014 * the intersections with the grid, and any edges that are present
1015 */
1016
1017 u32 icolours[] = { 0xffff00ff, 0xff00ffff, 0xffffff00 };
1018
1019 v3f pa, pb, pc, pd, pl0, pl1;
1020 pa[0] = wg->region[0][0] + (float)wg->cell_id[0] *k_gridscale;
1021 pa[1] = (wg->region[0][1] + wg->region[1][1]) * 0.5f + k_gridscale;
1022 pa[2] = wg->region[0][2] + (float)wg->cell_id[1] *k_gridscale;
1023 #if 0
1024 pb[0] = pa[0];
1025 pb[1] = pa[1];
1026 pb[2] = pa[2] + k_gridscale;
1027 pc[0] = pa[0] + k_gridscale;
1028 pc[1] = pa[1];
1029 pc[2] = pa[2] + k_gridscale;
1030 pd[0] = pa[0] + k_gridscale;
1031 pd[1] = pa[1];
1032 pd[2] = pa[2];
1033 /* if you want to draw the current cell */
1034 vg_line( pa, pb, 0xff00ffff );
1035 vg_line( pb, pc, 0xff00ffff );
1036 vg_line( pc, pd, 0xff00ffff );
1037 vg_line( pd, pa, 0xff00ffff );
1038 #endif
1039 pl0[0] = pa[0] + wg->pos[0]*k_gridscale;
1040 pl0[1] = pa[1];
1041 pl0[2] = pa[2] + wg->pos[1]*k_gridscale;
1042
1043 /*
1044 * If there are edges present, we need to create a 'substep' event, where
1045 * we find the intersection point, find the fully resolved position,
1046 * then the new pos dir is the intersection->resolution
1047 *
1048 * the resolution is applied in non-discretized space in order to create a
1049 * suitable vector for finding outflow, we want it to leave the cell so it
1050 * can be used by the quad
1051 */
1052
1053 v2f pos, dir;
1054 v2_copy( wg->pos, pos );
1055 v2_muls( wg->dir, wg->move, dir );
1056
1057 struct grid_sample *corners[4];
1058 v2f corners2d[4] = {{0.0f,0.0f},{0.0f,1.0f},{1.0f,1.0f},{1.0f,0.0f}};
1059 const struct conf *conf = player_walkgrid_conf( wg, wg->cell_id, corners );
1060
1061 float t[7];
1062 player_walkgrid_min_cell( t, pos, dir );
1063
1064 for( int i=0; i<conf->edge_count; i++ )
1065 {
1066 const struct confedge *edge = &conf->edges[i];
1067
1068 v2f e0, e1, n, r, target, res, tangent;
1069 e0[0] = corners2d[edge->i0][0] + corners[edge->d0]->clip[edge->a0][0];
1070 e0[1] = corners2d[edge->i0][1] + corners[edge->d0]->clip[edge->a0][2];
1071 e1[0] = corners2d[edge->i1][0] + corners[edge->d1]->clip[edge->a1][0];
1072 e1[1] = corners2d[edge->i1][1] + corners[edge->d1]->clip[edge->a1][2];
1073
1074 v3f pe0 = { pa[0] + e0[0]*k_gridscale,
1075 pa[1],
1076 pa[2] + e0[1]*k_gridscale };
1077 v3f pe1 = { pa[0] + e1[0]*k_gridscale,
1078 pa[1],
1079 pa[2] + e1[1]*k_gridscale };
1080
1081 v2_sub( e1, e0, tangent );
1082 n[0] = -tangent[1];
1083 n[1] = tangent[0];
1084 v2_normalize( n );
1085
1086 /*
1087 * If we find ourselfs already penetrating the edge, move back out a
1088 * little
1089 */
1090 v2_sub( e0, pos, r );
1091 float p1 = v2_dot(r,n);
1092
1093 if( -p1 < 0.0001f )
1094 {
1095 v2_muladds( pos, n, p1+0.0001f, pos );
1096 v2_copy( pos, wg->pos );
1097 v3f p_new = { pa[0] + pos[0]*k_gridscale,
1098 pa[1],
1099 pa[2] + pos[1]*k_gridscale };
1100 v3_copy( p_new, pl0 );
1101 }
1102
1103 v2_add( pos, dir, target );
1104
1105 v2f v1, v2, v3;
1106 v2_sub( e0, pos, v1 );
1107 v2_sub( target, pos, v2 );
1108
1109 v2_copy( n, v3 );
1110
1111 v2_sub( e0, target, r );
1112 float p = v2_dot(r,n),
1113 t1 = v2_dot(v1,v3)/v2_dot(v2,v3);
1114
1115 if( t1 < t[6] && t1 > 0.0f && -p < 0.001f )
1116 {
1117 v2_muladds( target, n, p+0.0001f, res );
1118
1119 v2f intersect;
1120 v2_muladds( pos, dir, t1, intersect );
1121 v2_copy( intersect, pos );
1122 v2_sub( res, intersect, dir );
1123
1124 v3f p_res = { pa[0] + res[0]*k_gridscale,
1125 pa[1],
1126 pa[2] + res[1]*k_gridscale };
1127 v3f p_int = { pa[0] + intersect[0]*k_gridscale,
1128 pa[1],
1129 pa[2] + intersect[1]*k_gridscale };
1130
1131 vg_line( pl0, p_int, icolours[iter%3] );
1132 v3_copy( p_int, pl0 );
1133 v2_copy( pos, wg->pos );
1134
1135 player_walkgrid_min_cell( t, pos, dir );
1136 }
1137 }
1138
1139 /*
1140 * Compute intersection with grid cell moving outwards
1141 */
1142 t[6] = vg_minf( t[6], 1.0f );
1143
1144 pl1[0] = pl0[0] + dir[0]*k_gridscale*t[6];
1145 pl1[1] = pl0[1];
1146 pl1[2] = pl0[2] + dir[1]*k_gridscale*t[6];
1147 vg_line( pl0, pl1, icolours[iter%3] );
1148
1149 if( t[6] < 1.0f )
1150 {
1151 /*
1152 * To figure out what t value created the clip so we know which edge
1153 * to wrap around
1154 */
1155
1156 if( t[4] < t[5] )
1157 {
1158 wg->pos[1] = pos[1] + dir[1]*t[6];
1159
1160 if( t[0] > t[1] ) /* left edge */
1161 {
1162 wg->pos[0] = 0.9999f;
1163 wg->cell_id[0] --;
1164
1165 if( wg->cell_id[0] == 0 )
1166 wg->move = -1.0f;
1167 }
1168 else /* Right edge */
1169 {
1170 wg->pos[0] = 0.0001f;
1171 wg->cell_id[0] ++;
1172
1173 if( wg->cell_id[0] == WALKGRID_SIZE-2 )
1174 wg->move = -1.0f;
1175 }
1176 }
1177 else
1178 {
1179 wg->pos[0] = pos[0] + dir[0]*t[6];
1180
1181 if( t[2] > t[3] ) /* bottom edge */
1182 {
1183 wg->pos[1] = 0.9999f;
1184 wg->cell_id[1] --;
1185
1186 if( wg->cell_id[1] == 0 )
1187 wg->move = -1.0f;
1188 }
1189 else /* top edge */
1190 {
1191 wg->pos[1] = 0.0001f;
1192 wg->cell_id[1] ++;
1193
1194 if( wg->cell_id[1] == WALKGRID_SIZE-2 )
1195 wg->move = -1.0f;
1196 }
1197 }
1198
1199 wg->move -= t[6];
1200 }
1201 else
1202 {
1203 v2_muladds( wg->pos, dir, wg->move, wg->pos );
1204 wg->move = 0.0f;
1205 }
1206 }
1207
1208 static void player_walkgrid_stand_cell(struct walkgrid *wg)
1209 {
1210 /*
1211 * NOTE: as opposed to the other function which is done in discretized space
1212 * this use a combination of both.
1213 */
1214
1215 v3f world;
1216 world[0] = wg->region[0][0]+((float)wg->cell_id[0]+wg->pos[0])*k_gridscale;
1217 world[1] = player.rb.co[1];
1218 world[2] = wg->region[0][2]+((float)wg->cell_id[1]+wg->pos[1])*k_gridscale;
1219
1220 struct grid_sample *corners[4];
1221 const struct conf *conf = player_walkgrid_conf( wg, wg->cell_id, corners );
1222
1223 if( conf != k_walkgrid_configs )
1224 {
1225 if( conf->edge_count == 0 )
1226 {
1227 v3f v0;
1228
1229 /* Split the basic quad along the shortest diagonal */
1230 if( fabsf(corners[2]->pos[1] - corners[0]->pos[1]) <
1231 fabsf(corners[3]->pos[1] - corners[1]->pos[1]) )
1232 {
1233 vg_line( corners[2]->pos, corners[0]->pos, 0xffaaaaaa );
1234
1235 if( wg->pos[0] > wg->pos[1] )
1236 player_walkgrid_stand_tri( corners[0]->pos,
1237 corners[3]->pos,
1238 corners[2]->pos, world );
1239 else
1240 player_walkgrid_stand_tri( corners[0]->pos,
1241 corners[2]->pos,
1242 corners[1]->pos, world );
1243 }
1244 else
1245 {
1246 vg_line( corners[3]->pos, corners[1]->pos, 0xffaaaaaa );
1247
1248 if( wg->pos[0] < 1.0f-wg->pos[1] )
1249 player_walkgrid_stand_tri( corners[0]->pos,
1250 corners[3]->pos,
1251 corners[1]->pos, world );
1252 else
1253 player_walkgrid_stand_tri( corners[3]->pos,
1254 corners[2]->pos,
1255 corners[1]->pos, world );
1256 }
1257 }
1258 else
1259 {
1260 for( int i=0; i<conf->edge_count; i++ )
1261 {
1262 const struct confedge *edge = &conf->edges[i];
1263
1264 v3f p0, p1;
1265 v3_muladds( corners[edge->i0]->pos,
1266 corners[edge->d0]->clip[edge->a0], k_gridscale, p0 );
1267 v3_muladds( corners[edge->i1]->pos,
1268 corners[edge->d1]->clip[edge->a1], k_gridscale, p1 );
1269
1270 /*
1271 * Find penetration distance between player position and the edge
1272 */
1273
1274 v2f normal = { -(p1[2]-p0[2]), p1[0]-p0[0] },
1275 rel = { world[0]-p0[0], world[2]-p0[2] };
1276
1277 if( edge->o0 == -1 )
1278 {
1279 /* No subregions (default case), just use triangle created by
1280 * i0, e0, e1 */
1281 player_walkgrid_stand_tri( corners[edge->i0]->pos,
1282 p0,
1283 p1, world );
1284 }
1285 else
1286 {
1287 /*
1288 * Test if we are in the first region, which is
1289 * edge.i0, edge.e0, edge.o0,
1290 */
1291 v3f v0, ref;
1292 v3_sub( p0, corners[edge->o0]->pos, ref );
1293 v3_sub( world, corners[edge->o0]->pos, v0 );
1294
1295 vg_line( corners[edge->o0]->pos, p0, 0xffffff00 );
1296 vg_line( corners[edge->o0]->pos, world, 0xff000000 );
1297
1298 if( ref[0]*v0[2] - ref[2]*v0[0] < 0.0f )
1299 {
1300 player_walkgrid_stand_tri( corners[edge->i0]->pos,
1301 p0,
1302 corners[edge->o0]->pos, world );
1303 }
1304 else
1305 {
1306 if( edge->o1 == -1 )
1307 {
1308 /*
1309 * No other edges mean we just need to use the opposite
1310 *
1311 * e0, e1, o0 (in our case, also i1)
1312 */
1313 player_walkgrid_stand_tri( p0,
1314 p1,
1315 corners[edge->o0]->pos, world );
1316 }
1317 else
1318 {
1319 /*
1320 * Note: this v0 calculation can be ommited with the
1321 * current tileset.
1322 *
1323 * the last two triangles we have are:
1324 * e0, e1, o1
1325 * and
1326 * e1, i1, o1
1327 */
1328 v3_sub( p1, corners[edge->o1]->pos, ref );
1329 v3_sub( world, corners[edge->o1]->pos, v0 );
1330 vg_line( corners[edge->o1]->pos, p1, 0xff00ffff );
1331
1332 if( ref[0]*v0[2] - ref[2]*v0[0] < 0.0f )
1333 {
1334 player_walkgrid_stand_tri( p0,
1335 p1,
1336 corners[edge->o1]->pos,
1337 world );
1338 }
1339 else
1340 {
1341 player_walkgrid_stand_tri( p1,
1342 corners[edge->i1]->pos,
1343 corners[edge->o1]->pos,
1344 world );
1345 }
1346 }
1347 }
1348 }
1349 }
1350 }
1351 }
1352
1353 v3_copy( world, player.rb.co );
1354 }
1355
1356 static void player_walkgrid_getsurface(void)
1357 {
1358 float const k_stepheight = 0.5f;
1359 float const k_miny = 0.6f;
1360 float const k_height = 1.78f;
1361 float const k_region_size = (float)WALKGRID_SIZE/2.0f * k_gridscale;
1362
1363 static struct walkgrid wg;
1364
1365 v3f cell;
1366 v3_copy( player.rb.co, cell );
1367 player_walkgrid_floor( cell );
1368
1369 v3_muladds( cell, (v3f){-1.0f,-1.0f,-1.0f}, k_region_size, wg.region[0] );
1370 v3_muladds( cell, (v3f){ 1.0f, 1.0f, 1.0f}, k_region_size, wg.region[1] );
1371
1372
1373 /*
1374 * Create player input vector
1375 */
1376 v3f delta = {0.0f,0.0f,0.0f};
1377 v3f fwd = { -sinf(-player.angles[0]), 0.0f, -cosf(-player.angles[0]) },
1378 side = { -fwd[2], 0.0f, fwd[0] };
1379
1380 /* Temp */
1381 if( !vg_console_enabled() )
1382 {
1383 if( glfwGetKey( vg_window, GLFW_KEY_W ) )
1384 v3_muladds( delta, fwd, ktimestep*k_walkspeed, delta );
1385 if( glfwGetKey( vg_window, GLFW_KEY_S ) )
1386 v3_muladds( delta, fwd, -ktimestep*k_walkspeed, delta );
1387
1388 if( glfwGetKey( vg_window, GLFW_KEY_A ) )
1389 v3_muladds( delta, side, -ktimestep*k_walkspeed, delta );
1390 if( glfwGetKey( vg_window, GLFW_KEY_D ) )
1391 v3_muladds( delta, side, ktimestep*k_walkspeed, delta );
1392
1393 v3_muladds( delta, fwd,
1394 vg_get_axis("vertical")*-ktimestep*k_walkspeed, delta );
1395 v3_muladds( delta, side,
1396 vg_get_axis("horizontal")*ktimestep*k_walkspeed, delta );
1397 }
1398
1399 /*
1400 * Create our move in grid space
1401 */
1402 wg.dir[0] = delta[0] * (1.0f/k_gridscale);
1403 wg.dir[1] = delta[2] * (1.0f/k_gridscale);
1404 wg.move = 1.0f;
1405
1406 v2f region_pos =
1407 {
1408 (player.rb.co[0] - wg.region[0][0]) * (1.0f/k_gridscale),
1409 (player.rb.co[2] - wg.region[0][2]) * (1.0f/k_gridscale)
1410 };
1411 v2f region_cell_pos;
1412 v2_floor( region_pos, region_cell_pos );
1413 v2_sub( region_pos, region_cell_pos, wg.pos );
1414
1415 wg.cell_id[0] = region_cell_pos[0];
1416 wg.cell_id[1] = region_cell_pos[1];
1417
1418 for(int y=0; y<WALKGRID_SIZE; y++ )
1419 {
1420 for(int x=0; x<WALKGRID_SIZE; x++ )
1421 {
1422 struct grid_sample *s = &wg.samples[y][x];
1423 v3_muladds( wg.region[0], (v3f){ x, 0, y }, k_gridscale, s->pos );
1424 s->state = k_traverse_none;
1425 s->type = k_sample_type_air;
1426 v3_zero( s->clip[0] );
1427 v3_zero( s->clip[1] );
1428 }
1429 }
1430
1431 v2i border[WALKGRID_SIZE*WALKGRID_SIZE];
1432 v2i *cborder = border;
1433 u32 border_length = 1;
1434
1435 struct grid_sample *base = NULL;
1436
1437 v2i starters[] = {{0,0},{1,1},{0,1},{1,0}};
1438
1439 for( int i=0;i<4;i++ )
1440 {
1441 v2i test;
1442 v2i_add( wg.cell_id, starters[i], test );
1443 v2i_copy( test, border[0] );
1444 base = &wg.samples[test[1]][test[0]];
1445
1446 base->pos[1] = cell[1];
1447 player_walkgrid_samplepole( base );
1448
1449 if( base->type == k_sample_type_valid )
1450 break;
1451 else
1452 base->type = k_sample_type_air;
1453 }
1454
1455 vg_line_pt3( base->pos, 0.1f, 0xffffffff );
1456
1457 int iter = 0;
1458
1459 while( border_length )
1460 {
1461 v2i directions[] = {{1,0},{0,1},{-1,0},{0,-1}};
1462
1463 v2i *old_border = cborder;
1464 int len = border_length;
1465
1466 border_length = 0;
1467 cborder = old_border+len;
1468
1469 for( int i=0; i<len; i++ )
1470 {
1471 v2i co;
1472 v2i_copy( old_border[i], co );
1473 struct grid_sample *sa = &wg.samples[co[1]][co[0]];
1474
1475 for( int j=0; j<4; j++ )
1476 {
1477 v2i newp;
1478 v2i_add( co, directions[j], newp );
1479
1480 if( newp[0] < 0 || newp[1] < 0 ||
1481 newp[0] == WALKGRID_SIZE || newp[1] == WALKGRID_SIZE )
1482 continue;
1483
1484 struct grid_sample *sb = &wg.samples[newp[1]][newp[0]];
1485 enum traverse_state thismove = j%2==0? 1: 2;
1486
1487 if( (sb->state & thismove) == 0x00 ||
1488 sb->type == k_sample_type_air )
1489 {
1490 sb->pos[1] = sa->pos[1];
1491
1492 player_walkgrid_samplepole( sb );
1493
1494 if( sb->type != k_sample_type_air )
1495 {
1496 /*
1497 * Need to do a blocker pass
1498 */
1499
1500 struct grid_sample *store = (j>>1 == 0)? sa: sb;
1501 player_walkgrid_clip_blocker( sa, sb, store, j%2 );
1502
1503
1504 if( sb->type != k_sample_type_air )
1505 {
1506 vg_line( sa->pos, sb->pos, 0xffffffff );
1507
1508 if( sb->state == k_traverse_none )
1509 v2i_copy( newp, cborder[ border_length ++ ] );
1510 }
1511 else
1512 {
1513 v3f p1;
1514 v3_muladds( sa->pos, store->clip[j%2], k_gridscale, p1 );
1515 vg_line( sa->pos, p1, 0xffffffff );
1516 }
1517 }
1518 else
1519 {
1520 /*
1521 * A clipping pass is now done on the edge of the walkable
1522 * surface
1523 */
1524
1525 struct grid_sample *store = (j>>1 == 0)? sa: sb;
1526 player_walkgrid_clip_edge( sa, sb, store, j%2 );
1527
1528 v3f p1;
1529 v3_muladds( sa->pos, store->clip[j%2], k_gridscale, p1 );
1530 vg_line( sa->pos, p1, 0xffffffff );
1531 }
1532
1533 sb->state |= thismove;
1534 }
1535 }
1536
1537 sa->state = k_traverse_h|k_traverse_v;
1538 }
1539
1540 iter ++;
1541 if( iter == walk_grid_iterations )
1542 break;
1543 }
1544
1545 /* Draw connections */
1546 struct grid_sample *corners[4];
1547 for( int x=0; x<WALKGRID_SIZE-1; x++ )
1548 {
1549 for( int z=0; z<WALKGRID_SIZE-1; z++ )
1550 {
1551 const struct conf *conf =
1552 player_walkgrid_conf( &wg, (v2i){x,z}, corners );
1553
1554 for( int i=0; i<conf->edge_count; i++ )
1555 {
1556 const struct confedge *edge = &conf->edges[i];
1557
1558 v3f p0, p1;
1559 v3_muladds( corners[edge->i0]->pos,
1560 corners[edge->d0]->clip[edge->a0], k_gridscale, p0 );
1561 v3_muladds( corners[edge->i1]->pos,
1562 corners[edge->d1]->clip[edge->a1], k_gridscale, p1 );
1563
1564 vg_line( p0, p1, 0xff0000ff );
1565 }
1566 }
1567 }
1568
1569 /*
1570 * Commit player movement into the grid
1571 */
1572
1573 if( v3_length2(delta) <= 0.00001f )
1574 return;
1575
1576 int i=0;
1577 for(; i<8 && wg.move > 0.001f; i++ )
1578 player_walkgrid_iter( &wg, i );
1579
1580 player_walkgrid_stand_cell( &wg );
1581 }
1582
1583 static void player_walkgrid(void)
1584 {
1585 player_walkgrid_getsurface();
1586
1587 m4x3_mulv( player.rb.to_world, (v3f){0.0f,1.8f,0.0f}, player.camera_pos );
1588 player_mouseview();
1589 rb_update_transform( &player.rb );
1590 }
1591
1592 /*
1593 * Animation
1594 */
1595
1596 static void player_animate(void)
1597 {
1598 /* Camera position */
1599 v3_sub( player.rb.v, player.v_last, player.a );
1600 v3_copy( player.rb.v, player.v_last );
1601
1602 v3_add( player.m, player.a, player.m );
1603 v3_lerp( player.m, (v3f){0.0f,0.0f,0.0f}, 0.1f, player.m );
1604
1605 player.m[0] = vg_clampf( player.m[0], -2.0f, 2.0f );
1606 player.m[1] = vg_clampf( player.m[1], -2.0f, 2.0f );
1607 player.m[2] = vg_clampf( player.m[2], -2.0f, 2.0f );
1608 v3_lerp( player.bob, player.m, 0.2f, player.bob );
1609
1610 /* Head */
1611 float lslip = fabsf(player.slip);
1612
1613 float kheight = 2.0f,
1614 kleg = 0.6f;
1615
1616 v3f offset;
1617 v3_zero( offset );
1618 m3x3_mulv( player.rb.to_local, player.bob, offset );
1619
1620 static float speed_wobble = 0.0f, speed_wobble_2 = 0.0f;
1621
1622 float kickspeed = vg_clampf(v3_length(player.rb.v)*(1.0f/40.0f), 0.0f, 1.0f);
1623 float kicks = (vg_randf()-0.5f)*2.0f*kickspeed;
1624 float sign = vg_signf( kicks );
1625 speed_wobble = vg_lerpf( speed_wobble, kicks*kicks*sign, 0.1f );
1626 speed_wobble_2 = vg_lerpf( speed_wobble_2, speed_wobble, 0.04f );
1627
1628 offset[0] *= 0.26f;
1629 offset[0] += speed_wobble_2*3.0f;
1630
1631 offset[1] *= -0.3f;
1632 offset[2] *= 0.01f;
1633
1634 offset[0] = vg_clampf( offset[0], -0.8f, 0.8f );
1635 offset[1] = vg_clampf( offset[1], -0.5f, 0.0f );
1636 offset[1] = 0.0f;
1637
1638 /*
1639 * Player rotation
1640 */
1641 #if 0
1642 float angle = v3_dot( player.rb.up, (v3f){0.0f,1.0f,0.0f} );
1643 v3f axis;
1644 v3_cross( player.rb.up, (v3f){0.0f,1.0f,0.0f}, axis );
1645
1646 v4f correction;
1647 if( angle < 0.99f && 0 )
1648 {
1649 m3x3_mulv( player.rb.to_local, axis, axis );
1650 q_axis_angle( correction, axis, acosf(angle) );
1651 }
1652 else
1653 {
1654 q_identity( correction );
1655 }
1656
1657 /*
1658 * Animation blending
1659 * ===========================================
1660 */
1661 #endif
1662
1663 static float fslide = 0.0f;
1664 static float fdirz = 0.0f;
1665 static float fdirx = 0.0f;
1666 static float fstand = 0.0f;
1667 static float ffly = 0.0f;
1668 static float fpush = 0.0f;
1669
1670 float speed = v3_length( player.rb.v );
1671
1672 fstand = vg_lerpf(fstand, 1.0f-vg_clampf(speed*0.03f,0.0f,1.0f),0.1f);
1673 fslide = vg_lerpf(fslide, vg_clampf(lslip,0.0f,1.0f), 0.04f);
1674 fdirz = vg_lerpf(fdirz, player.reverse > 0.0f? 0.0f: 1.0f, 0.04f );
1675 fdirx = vg_lerpf(fdirx, player.slip < 0.0f? 0.0f: 1.0f, 0.01f );
1676 ffly = vg_lerpf(ffly, player.in_air? 1.0f: 0.0f, 0.04f );
1677 fpush = vg_lerpf(fpush, player.pushing, 0.1f );
1678
1679 float lr = fdirz * (15.0f/30.0f),
1680 st = offset[1]*-2.0f,
1681 sa = fdirx * (15.0f/30.0f);
1682
1683 mdl_keyframe apose[32], bpose[32];
1684 skeleton_sample_anim( &player.mdl.sk, player.mdl.anim_stand, lr, apose );
1685 skeleton_sample_anim( &player.mdl.sk, player.mdl.anim_highg, lr, bpose );
1686 skeleton_lerp_pose( &player.mdl.sk, apose, bpose, st, apose );
1687
1688 skeleton_sample_anim( &player.mdl.sk, player.mdl.anim_slide, sa, bpose );
1689 skeleton_lerp_pose( &player.mdl.sk, apose, bpose, fslide, apose );
1690
1691 static float fairdir = 0.0f;
1692 fairdir = vg_lerpf( fairdir, -vg_get_axis("horizontal"), 0.04f );
1693
1694 /* air anim */
1695 float air_dir = (fairdir*0.5f+0.5f)*(15.0f/30.0f);
1696 skeleton_sample_anim( &player.mdl.sk, player.mdl.anim_air, air_dir, bpose );
1697 skeleton_lerp_pose( &player.mdl.sk, apose, bpose, ffly, apose );
1698
1699 /* push anim */
1700 skeleton_sample_anim( &player.mdl.sk, player.reverse > 0.0f?
1701 player.mdl.anim_push:
1702 player.mdl.anim_push_reverse,
1703 player.push_time, bpose );
1704 skeleton_lerp_pose( &player.mdl.sk, apose, bpose, fpush, apose );
1705
1706
1707 /* additive effects */
1708 apose[player.mdl.id_hip-1].co[0] += offset[0];
1709 apose[player.mdl.id_hip-1].co[2] += offset[2];
1710 apose[player.mdl.id_ik_hand_l-1].co[0] += offset[0];
1711 apose[player.mdl.id_ik_hand_l-1].co[2] += offset[2];
1712 apose[player.mdl.id_ik_hand_r-1].co[0] += offset[0];
1713 apose[player.mdl.id_ik_hand_r-1].co[2] += offset[2];
1714 apose[player.mdl.id_ik_elbow_l-1].co[0] += offset[0];
1715 apose[player.mdl.id_ik_elbow_l-1].co[2] += offset[2];
1716 apose[player.mdl.id_ik_elbow_r-1].co[0] += offset[0];
1717 apose[player.mdl.id_ik_elbow_r-1].co[2] += offset[2];
1718
1719 skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_defer_ik );
1720 skeleton_apply_ik_pass( &player.mdl.sk );
1721 skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_deffered_only );
1722
1723 v3_copy( player.mdl.sk.final_mtx[player.mdl.id_head-1][3],
1724 player.mdl.cam_pos );
1725 skeleton_apply_inverses( &player.mdl.sk );
1726 skeleton_apply_transform( &player.mdl.sk, player.rb.to_world );
1727
1728 skeleton_debug( &player.mdl.sk );
1729
1730 #if 0
1731 character_pose_reset( &player.mdl );
1732
1733 /* TODO */
1734 float fstand1 = 1.0f-(1.0f-fstand)*0.0f;
1735
1736 float amt_air = ffly*ffly,
1737 amt_ground = 1.0f-amt_air,
1738 amt_std = (1.0f-fslide) * amt_ground,
1739 amt_stand = amt_std * fstand1,
1740 amt_aero = amt_std * (1.0f-fstand1),
1741 amt_slide = amt_ground * fslide;
1742
1743 character_final_pose( &player.mdl, offset, &pose_stand, amt_stand*fdirz );
1744 character_final_pose( &player.mdl, offset,
1745 &pose_stand_reverse, amt_stand * (1.0f-fdirz) );
1746
1747 character_final_pose( &player.mdl, offset, &pose_aero, amt_aero*fdirz );
1748 character_final_pose( &player.mdl, offset,
1749 &pose_aero_reverse, amt_aero * (1.0f-fdirz) );
1750
1751 character_final_pose( &player.mdl, offset, &pose_slide, amt_slide*fdirx );
1752 character_final_pose( &player.mdl, offset,
1753 &pose_slide1, amt_slide*(1.0f-fdirx) );
1754
1755 character_final_pose( &player.mdl, (v4f){0.0f,0.0f,0.0f,1.0f},
1756 &pose_fly, amt_air );
1757
1758 /*
1759 * Additive effects
1760 * ==========================
1761 */
1762 struct ik_basic *arm_l = &player.mdl.ik_arm_l,
1763 *arm_r = &player.mdl.ik_arm_r;
1764
1765 v3f localv;
1766 m3x3_mulv( player.rb.to_local, player.rb.v, localv );
1767
1768 /* New board transformation */
1769 v4f board_rotation; v3f board_location;
1770
1771 v4f rz, rx;
1772 q_axis_angle( rz, (v3f){ 0.0f, 0.0f, 1.0f }, player.board_xy[0] );
1773 q_axis_angle( rx, (v3f){ 1.0f, 0.0f, 0.0f }, player.board_xy[1] );
1774 q_mul( rx, rz, board_rotation );
1775
1776 v3f *mboard = player.mdl.matrices[k_chpart_board];// player.mboard;
1777 q_m3x3( board_rotation, mboard );
1778 m3x3_mulv( mboard, (v3f){ 0.0f, -0.5f, 0.0f }, board_location );
1779 v3_add( (v3f){0.0f,0.5f,0.0f}, board_location, board_location );
1780 v3_copy( board_location, mboard[3] );
1781
1782
1783 float wheel_r = offset[0]*-0.4f;
1784 v4f qwheel;
1785 q_axis_angle( qwheel, (v3f){0.0f,1.0f,0.0f}, wheel_r );
1786
1787 q_m3x3( qwheel, player.mdl.matrices[k_chpart_wb] );
1788
1789 m3x3_transpose( player.mdl.matrices[k_chpart_wb],
1790 player.mdl.matrices[k_chpart_wf] );
1791 v3_copy( player.mdl.offsets[k_chpart_wb],
1792 player.mdl.matrices[k_chpart_wb][3] );
1793 v3_copy( player.mdl.offsets[k_chpart_wf],
1794 player.mdl.matrices[k_chpart_wf][3] );
1795
1796 m4x3_mul( mboard, player.mdl.matrices[k_chpart_wb],
1797 player.mdl.matrices[k_chpart_wb] );
1798 m4x3_mul( mboard, player.mdl.matrices[k_chpart_wf],
1799 player.mdl.matrices[k_chpart_wf] );
1800
1801 m4x3_mulv( mboard, player.mdl.ik_leg_l.end, player.mdl.ik_leg_l.end );
1802 m4x3_mulv( mboard, player.mdl.ik_leg_r.end, player.mdl.ik_leg_r.end );
1803
1804
1805 v3_copy( player.mdl.ik_arm_l.end, player.handl_target );
1806 v3_copy( player.mdl.ik_arm_r.end, player.handr_target );
1807
1808 if( 1||player.in_air )
1809 {
1810 float tuck = player.board_xy[1],
1811 tuck_amt = fabsf( tuck ) * (1.0f-fabsf(player.board_xy[0]));
1812
1813 float crouch = player.grab*0.3f;
1814 v3_muladds( player.mdl.ik_body.base, (v3f){0.0f,-1.0f,0.0f},
1815 crouch, player.mdl.ik_body.base );
1816 v3_muladds( player.mdl.ik_body.end, (v3f){0.0f,-1.0f,0.0f},
1817 crouch*1.2f, player.mdl.ik_body.end );
1818
1819 if( tuck < 0.0f )
1820 {
1821 //foot_l *= 1.0f-tuck_amt*1.5f;
1822
1823 if( player.grab > 0.1f )
1824 {
1825 m4x3_mulv( mboard, (v3f){0.1f,0.14f,0.6f},
1826 player.handl_target );
1827 }
1828 }
1829 else
1830 {
1831 //foot_r *= 1.0f-tuck_amt*1.4f;
1832
1833 if( player.grab > 0.1f )
1834 {
1835 m4x3_mulv( mboard, (v3f){0.1f,0.14f,-0.6f},
1836 player.handr_target );
1837 }
1838 }
1839 }
1840
1841 v3_lerp( player.handl, player.handl_target, 1.0f, player.handl );
1842 v3_lerp( player.handr, player.handr_target, 1.0f, player.handr );
1843
1844 v3_copy( player.handl, player.mdl.ik_arm_l.end );
1845 v3_copy( player.handr, player.mdl.ik_arm_r.end );
1846
1847 /* Head rotation */
1848
1849 static float rhead = 0.0f;
1850 static const float klook_max = 0.8f;
1851 rhead = vg_lerpf( rhead,
1852 vg_clampf( atan2f(localv[2],-localv[0]),-klook_max,klook_max), 0.04f );
1853 player.mdl.rhead = rhead;
1854 #endif
1855 }
1856
1857 static void player_camera_update(void)
1858 {
1859 /* Update camera matrices */
1860 m4x3_identity( player.camera );
1861 m4x3_rotate_y( player.camera, -player.angles[0] );
1862 m4x3_rotate_x( player.camera, -player.angles[1] );
1863 v3_copy( player.camera_pos, player.camera[3] );
1864 m4x3_invert_affine( player.camera, player.camera_inverse );
1865 }
1866
1867 static void player_animate_death_cam(void)
1868 {
1869 #if 0
1870 v3f delta;
1871 v3f head_pos;
1872 v3_copy( player.mdl.ragdoll[k_chpart_head].co, head_pos );
1873
1874 v3_sub( head_pos, player.camera_pos, delta );
1875 v3_normalize( delta );
1876
1877 v3f follow_pos;
1878 v3_muladds( head_pos, delta, -2.5f, follow_pos );
1879 v3_lerp( player.camera_pos, follow_pos, 0.1f, player.camera_pos );
1880
1881 /*
1882 * Make sure the camera stays above the ground
1883 */
1884 v3f min_height = {0.0f,1.0f,0.0f};
1885
1886 v3f sample;
1887 v3_add( player.camera_pos, min_height, sample );
1888 ray_hit hit;
1889 hit.dist = min_height[1]*2.0f;
1890
1891 if( ray_world( sample, (v3f){0.0f,-1.0f,0.0f}, &hit ))
1892 v3_add( hit.pos, min_height, player.camera_pos );
1893
1894 player.camera_pos[1] =
1895 vg_maxf( wrender.height + 2.0f, player.camera_pos[1] );
1896
1897 player.angles[0] = atan2f( delta[0], -delta[2] );
1898 player.angles[1] = -asinf( delta[1] );
1899 #endif
1900 }
1901
1902 static void player_animate_camera(void)
1903 {
1904 static v3f lerp_cam = {0.0f,0.0f,0.0f};
1905 v3f offs = { -0.4f, 0.15f, 0.0f };
1906
1907 v3_lerp( lerp_cam, player.mdl.cam_pos, 0.8f, lerp_cam );
1908 v3_add( lerp_cam, offs, offs );
1909 m4x3_mulv( player.rb.to_world, offs, player.camera_pos );
1910
1911 /* Look angles */
1912 v3_lerp( player.vl, player.rb.v, 0.05f, player.vl );
1913
1914 float yaw = atan2f( player.vl[0], -player.vl[2] ),
1915 pitch = atan2f( -player.vl[1],
1916 sqrtf(
1917 player.vl[0]*player.vl[0] + player.vl[2]*player.vl[2]
1918 )) * 0.7f;
1919
1920 player.angles[0] = yaw;
1921 player.angles[1] = pitch + 0.30f;
1922
1923 /* Camera shake */
1924 static v2f shake_damp = {0.0f,0.0f};
1925 v2f shake = { vg_randf()-0.5f, vg_randf()-0.5f };
1926 v2_muls( shake, v3_length(player.rb.v)*0.3f
1927 * (1.0f+fabsf(player.slip)), shake);
1928
1929 v2_lerp( shake_damp, shake, 0.01f, shake_damp );
1930 shake_damp[0] *= 0.2f;
1931
1932 v2_muladds( player.angles, shake_damp, 0.1f, player.angles );
1933 }
1934
1935 /*
1936 * Audio
1937 */
1938 static void player_audio(void)
1939 {
1940 float speed = vg_minf(v3_length( player.rb.v )*0.1f,1.0f),
1941 attn = v3_dist( player.rb.co, player.camera[3] )+1.0f;
1942 attn = (1.0f/(attn*attn)) * speed;
1943
1944 static float air = 0.0f;
1945 air = vg_lerpf(air, player.in_air? 1.0f: 0.0f, 0.7f);
1946
1947 v3f ears = { 1.0f,0.0f,0.0f };
1948 v3f delta;
1949
1950 v3_sub( player.rb.co, player.camera[3], delta );
1951 v3_normalize( delta );
1952 m3x3_mulv( player.camera, ears, ears );
1953
1954 float pan = v3_dot( ears, delta );
1955 audio_player0.pan = pan;
1956 audio_player1.pan = pan;
1957 audio_player2.pan = pan;
1958
1959 if( freecam )
1960 {
1961 audio_player0.vol = 0.0f;
1962 audio_player1.vol = 0.0f;
1963 audio_player2.vol = 0.0f;
1964 }
1965 else
1966 {
1967 if( player.is_dead )
1968 {
1969 audio_player0.vol = 0.0f;
1970 audio_player1.vol = 0.0f;
1971 audio_player2.vol = 0.0f;
1972 }
1973 else
1974 {
1975 float slide = vg_clampf( fabsf(player.slip), 0.0f, 1.0f );
1976 audio_player0.vol = (1.0f-air)*attn*(1.0f-slide);
1977 audio_player1.vol = air *attn;
1978 audio_player2.vol = (1.0f-air)*attn*slide;
1979 }
1980 }
1981 }
1982
1983 /*
1984 * Public Endpoints
1985 */
1986 static float *player_cam_pos(void)
1987 {
1988 return player.camera_pos;
1989 }
1990
1991 static int reset_player( int argc, char const *argv[] )
1992 {
1993 struct respawn_point *rp = NULL, *r;
1994
1995 if( argc == 1 )
1996 {
1997 for( int i=0; i<world.spawn_count; i++ )
1998 {
1999 r = &world.spawns[i];
2000 if( !strcmp( r->name, argv[0] ) )
2001 {
2002 rp = r;
2003 break;
2004 }
2005 }
2006
2007 if( !rp )
2008 vg_warn( "No spawn named '%s'\n", argv[0] );
2009 }
2010
2011 if( !rp )
2012 {
2013 float min_dist = INFINITY;
2014
2015 for( int i=0; i<world.spawn_count; i++ )
2016 {
2017 r = &world.spawns[i];
2018 float d = v3_dist2( r->co, player.rb.co );
2019
2020 vg_info( "Dist %s : %f\n", r->name, d );
2021 if( d < min_dist )
2022 {
2023 min_dist = d;
2024 rp = r;
2025 }
2026 }
2027 }
2028
2029 if( !rp )
2030 {
2031 vg_error( "No spawn found\n" );
2032 if( !world.spawn_count )
2033 return 0;
2034
2035 rp = &world.spawns[0];
2036 }
2037
2038 v4_copy( rp->q, player.rb.q );
2039 v3_copy( rp->co, player.rb.co );
2040
2041 player.vswitch = 1.0f;
2042 player.slip_last = 0.0f;
2043 player.is_dead = 0;
2044 player.in_air = 1;
2045 m3x3_identity( player.vr );
2046
2047 player.mdl.shoes[0] = 1;
2048 player.mdl.shoes[1] = 1;
2049
2050 rb_update_transform( &player.rb );
2051 m3x3_mulv( player.rb.to_world, (v3f){ 0.0f, 0.0f, -1.2f }, player.rb.v );
2052
2053 player.rb_gate_frame = player.rb;
2054 return 1;
2055 }
2056
2057 static void player_update(void)
2058 {
2059 for( int i=0; i<player.land_log_count; i++ )
2060 draw_cross( player.land_target_log[i],
2061 player.land_target_colours[i], 0.25f);
2062
2063 if( vg_get_axis("grabl")>0.0f)
2064 {
2065 player.rb = player.rb_gate_frame;
2066 player.is_dead = 0;
2067 player.in_air = 1;
2068 m3x3_identity( player.vr );
2069
2070 player.mdl.shoes[0] = 1;
2071 player.mdl.shoes[1] = 1;
2072
2073 world_routes_notify_reset();
2074 }
2075
2076 if( vg_get_button_down( "switchmode" ) )
2077 {
2078 player.on_board ^= 0x1;
2079 }
2080
2081 if( player.is_dead )
2082 {
2083 character_ragdoll_iter( &player.mdl );
2084 character_debug_ragdoll( &player.mdl );
2085
2086 if( !freecam )
2087 player_animate_death_cam();
2088 }
2089 else
2090 {
2091 if( player.on_board )
2092 {
2093 player_do_motion();
2094 player_animate();
2095
2096 if( !freecam )
2097 player_animate_camera();
2098 }
2099 else
2100 {
2101 player_walkgrid();
2102 }
2103 }
2104
2105 if( freecam )
2106 player_freecam();
2107
2108 player_camera_update();
2109 player_audio();
2110 }
2111
2112 static void draw_player(void)
2113 {
2114 /* Draw */
2115 #if 0
2116 m4x3_copy( player.rb.to_world, player.mdl.mroot );
2117
2118 if( player.is_dead )
2119 character_mimic_ragdoll( &player.mdl );
2120 else
2121 character_eval( &player.mdl );
2122
2123 float opacity = 1.0f-player.air_blend;
2124 if( player.is_dead )
2125 opacity = 0.0f;
2126
2127 character_draw( &player.mdl, opacity, player.camera );
2128 #endif
2129
2130 shader_viewchar_use();
2131 vg_tex2d_bind( &tex_characters, 0 );
2132 shader_viewchar_uTexMain( 0 );
2133 shader_viewchar_uCamera( player.camera[3] );
2134 shader_viewchar_uPv( vg_pv );
2135 shader_link_standard_ub( _shader_viewchar.id, 2 );
2136 glUniformMatrix4x3fv( _uniform_viewchar_uTransforms,
2137 player.mdl.sk.bone_count,
2138 0,
2139 (float *)player.mdl.sk.final_mtx );
2140
2141 mesh_bind( &player.mdl.mesh );
2142 mesh_draw( &player.mdl.mesh );
2143 }
2144
2145 #endif /* PLAYER_H */