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