bvh interface and high perf gate
[carveJwlIkooP6JGAAIwe30JlM.git] / player.h
1 #ifndef PLAYER_H
2 #define PLAYER_H
3
4 #include "common.h"
5 #include "character.h"
6
7 static int freecam = 0;
8 static float k_walkspeed = 2.0f;
9 static int walk_grid_iterations = 1;
10
11 static struct gplayer
12 {
13 /* Physics */
14 v3f co, v, a, v_last, m, bob, vl;
15 v4f rot;
16 float vswitch, slip, slip_last,
17 reverse;
18
19 float iY; /* Yaw inertia */
20 int in_air, is_dead, on_board;
21
22 /* Input */
23 v2f joy_l;
24
25 v2f board_xy;
26 float grab;
27 float pitch;
28
29 v3f land_target;
30 v3f land_target_log[22];
31 u32 land_target_colours[22];
32 int land_log_count;
33 m3x3f vr;
34
35 m4x3f to_world, to_local;
36
37 struct character mdl;
38
39 v3f handl_target, handr_target,
40 handl, handr;
41
42 /* Camera */
43 float air_blend;
44
45 v3f camera_pos, smooth_localcam;
46 v2f angles;
47 m4x3f camera, camera_inverse;
48 }
49 player =
50 {
51 .on_board = 1
52 };
53
54 static void player_transform_update(void)
55 {
56 q_normalize( player.rot );
57 q_m3x3( player.rot, player.to_world );
58 v3_copy( player.co, player.to_world[3] );
59
60 m4x3_invert_affine( player.to_world, player.to_local );
61 }
62
63 static int reset_player( int argc, char const *argv[] )
64 {
65 v3_copy( (v3f){ 0.0f, -2.0f, 0.0f }, player.co );
66
67 if( argc == 1 )
68 {
69 if( !strcmp( argv[0], "tutorial" ))
70 v3_copy( world.tutorial, player.co );
71 }
72
73 v3_copy( (v3f){ 0.0f, 0.0f, -0.2f }, player.v );
74 q_identity( player.rot );
75 player.vswitch = 1.0f;
76 player.slip_last = 0.0f;
77 player.is_dead = 0;
78 player.in_air = 1;
79 m3x3_identity( player.vr );
80
81 player.mdl.shoes[0] = 1;
82 player.mdl.shoes[1] = 1;
83
84 player_transform_update();
85 return 0;
86 }
87
88 static void player_mouseview(void)
89 {
90 static v2f mouse_last,
91 view_vel = { 0.0f, 0.0f };
92
93 if( vg_get_button_down( "primary" ) )
94 v2_copy( vg_mouse, mouse_last );
95 else if( vg_get_button( "primary" ) )
96 {
97 v2f delta;
98 v2_sub( vg_mouse, mouse_last, delta );
99 v2_copy( vg_mouse, mouse_last );
100
101 v2_muladds( view_vel, delta, 0.005f, view_vel );
102 }
103
104 v2_muls( view_vel, 0.7f, view_vel );
105 v2_add( view_vel, player.angles, player.angles );
106 player.angles[1] = vg_clampf( player.angles[1], -VG_PIf*0.5f, VG_PIf*0.5f );
107
108 }
109
110 static void player_freecam(void)
111 {
112 player_mouseview();
113
114 float movespeed = 25.0f;
115 v3f lookdir = { 0.0f, 0.0f, -1.0f },
116 sidedir = { 1.0f, 0.0f, 0.0f };
117
118 m3x3_mulv( player.camera, lookdir, lookdir );
119 m3x3_mulv( player.camera, sidedir, sidedir );
120
121 static v3f move_vel = { 0.0f, 0.0f, 0.0f };
122 if( vg_get_button( "forward" ) )
123 v3_muladds( move_vel, lookdir, ktimestep * movespeed, move_vel );
124 if( vg_get_button( "back" ) )
125 v3_muladds( move_vel, lookdir, ktimestep *-movespeed, move_vel );
126 if( vg_get_button( "left" ) )
127 v3_muladds( move_vel, sidedir, ktimestep *-movespeed, move_vel );
128 if( vg_get_button( "right" ) )
129 v3_muladds( move_vel, sidedir, ktimestep * movespeed, move_vel );
130
131 v3_muls( move_vel, 0.7f, move_vel );
132 v3_add( move_vel, player.camera_pos, player.camera_pos );
133 }
134
135 static void apply_gravity( v3f vel, float const timestep )
136 {
137 v3f gravity = { 0.0f, -9.6f, 0.0f };
138 v3_muladds( vel, gravity, timestep, vel );
139 }
140
141 /*
142 * TODO: The angle bias should become greater when launching from a steeper
143 * angle and skewed towords more 'downwards' angles when launching from
144 * shallower trajectories
145 */
146 static void player_start_air(void)
147 {
148 player.in_air = 1;
149
150 float pstep = ktimestep*10.0f;
151
152 float best_velocity_mod = 0.0f,
153 best_velocity_delta = -9999.9f;
154
155 float k_bias = 0.97f;
156
157 v3f axis, vup;
158 m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, vup );
159 v3_cross( vup, player.v, axis );
160 v3_normalize( axis );
161 player.land_log_count = 0;
162
163 m3x3_identity( player.vr );
164
165 for( int m=-3;m<=12; m++ )
166 {
167 float vmod = ((float)m / 15.0f)*0.09f;
168
169 v3f pco, pco1, pv;
170 v3_copy( player.co, pco );
171 v3_muls( player.v, k_bias, pv );
172
173 /*
174 * Try different 'rotations' of the velocity to find the best possible
175 * landing normal. This conserves magnitude at the expense of slightly
176 * unrealistic results
177 */
178
179 m3x3f vr;
180 v4f vr_q;
181
182 q_axis_angle( vr_q, axis, vmod );
183 q_m3x3( vr_q, vr );
184
185 m3x3_mulv( vr, pv, pv );
186 v3_muladds( pco, pv, pstep, pco );
187
188 for( int i=0; i<50; i++ )
189 {
190 v3_copy( pco, pco1 );
191 apply_gravity( pv, pstep );
192
193 m3x3_mulv( vr, pv, pv );
194 v3_muladds( pco, pv, pstep, pco );
195
196 ray_hit contact;
197 v3f vdir;
198
199 v3_sub( pco, pco1, vdir );
200 contact.dist = v3_length( vdir );
201 v3_divs( vdir, contact.dist, vdir);
202
203 if( ray_world( pco1, vdir, &contact ))
204 {
205 float land_delta = v3_dot( pv, contact.normal );
206 u32 scolour = (u8)(vg_minf(-land_delta * 2.0f, 255.0f));
207
208 /* Bias prediction towords ramps */
209 if( ray_hit_is_ramp( &contact ) )
210 {
211 land_delta *= 0.1f;
212 scolour |= 0x0000a000;
213 }
214
215 if( (land_delta < 0.0f) && (land_delta > best_velocity_delta) )
216 {
217 best_velocity_delta = land_delta;
218 best_velocity_mod = vmod;
219
220 v3_copy( contact.pos, player.land_target );
221
222 q_axis_angle( vr_q, axis, vmod*0.1f );
223 q_m3x3( vr_q, player.vr );
224 }
225
226 v3_copy( contact.pos,
227 player.land_target_log[player.land_log_count] );
228 player.land_target_colours[player.land_log_count] =
229 0xff000000 | scolour;
230
231 player.land_log_count ++;
232
233 break;
234 }
235 }
236 }
237
238 //v3_rotate( player.v, best_velocity_mod, axis, player.v );
239
240 return;
241 v3_muls( player.v, best_velocity_mod, player.v );
242 }
243
244 static int sample_if_resistant( v3f pos )
245 {
246 v3f ground;
247 v3_copy( pos, ground );
248 ground[1] += 4.0f;
249
250 ray_hit hit;
251 hit.dist = INFINITY;
252
253 if( ray_world( ground, (v3f){0.0f,-1.0f,0.0f}, &hit ))
254 {
255 v3f angle;
256 v3_copy( player.v, angle );
257 v3_normalize( angle );
258 float resistance = v3_dot( hit.normal, angle );
259
260 if( resistance < 0.25f )
261 {
262 v3_copy( hit.pos, pos );
263 return 1;
264 }
265 }
266
267 return 0;
268 }
269
270 static float stable_force( float current, float diff )
271 {
272 float new = current + diff;
273
274 if( new * current < 0.0f )
275 return 0.0f;
276
277 return new;
278 }
279
280 static void player_physics_ground(void)
281 {
282 /*
283 * Getting surface collision points,
284 * the contact manifold is a triangle for simplicity.
285 */
286 v3f contact_front, contact_back, contact_norm, vup, vside,
287 axis;
288
289 float klength = 0.65f;
290 m4x3_mulv( player.to_world, (v3f){ 0.15f,0.0f,-klength}, contact_norm );
291 m4x3_mulv( player.to_world, (v3f){-0.15f,0.0f,-klength}, contact_front );
292 m4x3_mulv( player.to_world, (v3f){ 0.00f,0.0f, klength}, contact_back );
293 m3x3_mulv( player.to_world, (v3f){ 0.0f, 1.0f, 0.0f}, vup );
294 m3x3_mulv( player.to_world, (v3f){ 1.0f, 0.0f, 0.0f}, vside );
295
296 v3f cn0, cn1, cn2;
297
298 int contact_count =
299 sample_if_resistant( contact_front ) +
300 sample_if_resistant( contact_back ) +
301 sample_if_resistant( contact_norm );
302
303 if( contact_count < 3 )
304 {
305 player_start_air();
306 return;
307 }
308
309 v3f norm;
310 v3f v0, v1;
311 v3_sub( contact_norm, contact_front, v0 );
312 v3_sub( contact_back, contact_front, v1 );
313 v3_cross( v1, v0, norm );
314 v3_normalize( norm );
315
316 vg_line( contact_norm, contact_front, 0xff00ff00 );
317 vg_line( contact_back, contact_front, 0xff0000ff );
318
319 /* Surface alignment */
320 float angle = v3_dot( vup, norm );
321 v3_cross( vup, norm, axis );
322
323 if( angle < 0.999f )
324 {
325 v4f correction;
326 q_axis_angle( correction, axis, acosf(angle) );
327 q_mul( correction, player.rot, player.rot );
328 }
329
330 float resistance = v3_dot( norm, player.v );
331 if( resistance >= 0.0f )
332 {
333 player_start_air();
334 return;
335 }
336 else
337 {
338 v3_muladds( player.v, norm, -resistance, player.v );
339 }
340
341 /* This is where velocity integration used to be */
342
343 float slip = 0.0f;
344
345 player.co[1] = (contact_front[1]+contact_back[1])*0.5f;
346
347 v3f vel;
348 m3x3_mulv( player.to_local, player.v, vel );
349
350 /* Calculate local forces */
351
352 if( fabsf(vel[2]) > 0.01f )
353 slip = fabsf(-vel[0] / vel[2]) * vg_signf(vel[0]);
354
355 if( fabsf( slip ) > 1.2f )
356 slip = vg_signf( slip ) * 1.2f;
357 player.slip = slip;
358 player.reverse = -vg_signf(vel[2]);
359
360 float substep = ktimestep * 0.2f;
361 float fwd_resistance = (vg_get_button( "break" )? 5.0f: 0.02f) * -substep;
362
363 for( int i=0; i<5; i++ )
364 {
365 vel[2] = stable_force( vel[2], vg_signf( vel[2] ) * fwd_resistance );
366 vel[0] = stable_force( vel[0], vg_signf( vel[0] ) * -7.0f *substep );
367 }
368
369 static double start_push = 0.0;
370 if( vg_get_button_down( "push" ) )
371 start_push = vg_time;
372
373 if( !vg_get_button("break") && vg_get_button( "push" ) )
374 {
375 float const k_maxpush = 16.0f,
376 k_pushaccel = 5.0f;
377
378 float cycle_time = vg_time-start_push,
379 amt = k_pushaccel * (sinf( cycle_time * 8.0f )*0.5f+0.5f)*ktimestep,
380 current = v3_length( vel ),
381 new_vel = vg_minf( current + amt, k_maxpush );
382 new_vel -= vg_minf(current, k_maxpush);
383 vel[2] -= new_vel * player.reverse;
384 }
385
386 m3x3_mulv( player.to_world, vel, player.v );
387
388 if( vg_get_button( "yawl" ) )
389 player.iY += 3.6f * ktimestep;
390 if( vg_get_button( "yawr" ) )
391 player.iY -= 3.6f * ktimestep;
392
393 float steer = vg_get_axis( "horizontal" );
394 player.iY -= vg_signf(steer)*powf(steer,2.0f) * 1.5f * ktimestep;
395
396 /* Too much lean and it starts to look like a snowboard here */
397 v2_lerp( player.board_xy, (v2f){ slip*0.25f, 0.0f },
398 ktimestep*5.0f, player.board_xy);
399 }
400
401 static void draw_cross(v3f pos,u32 colour, float scale)
402 {
403 v3f p0, p1;
404 v3_add( (v3f){ scale,0.0f,0.0f}, pos, p0 );
405 v3_add( (v3f){-scale,0.0f,0.0f}, pos, p1 );
406 vg_line( p0, p1, colour );
407 v3_add( (v3f){0.0f, scale,0.0f}, pos, p0 );
408 v3_add( (v3f){0.0f,-scale,0.0f}, pos, p1 );
409 vg_line( p0, p1, colour );
410 v3_add( (v3f){0.0f,0.0f, scale}, pos, p0 );
411 v3_add( (v3f){0.0f,0.0f,-scale}, pos, p1 );
412 vg_line( p0, p1, colour );
413 }
414
415 static void player_physics_air(void)
416 {
417 m3x3_mulv( player.vr, player.v, player.v );
418 draw_cross( player.land_target, 0xff0000ff, 1 );
419
420 v3f ground_pos;
421 v3_copy( player.co, ground_pos );
422 ground_pos[1] += 4.0f;
423
424 ray_hit hit;
425 hit.dist = INFINITY;
426 if( ray_world( ground_pos, (v3f){0.0f,-1.0f,0.0f}, &hit ))
427 {
428 if( hit.pos[1] > player.co[1] )
429 {
430 player.in_air = 0;
431
432 if( !ray_hit_is_ramp( &hit ) )
433 {
434 player.is_dead = 1;
435 character_ragdoll_copypose( &player.mdl, player.v );
436 }
437
438 return;
439 }
440 }
441
442 /* Prediction
443 */
444 float pstep = ktimestep*10.0f;
445
446 v3f pco, pco1, pv;
447 v3_copy( player.co, pco );
448 v3_copy( player.v, pv );
449
450 float time_to_impact = 0.0f;
451 float limiter = 1.0f;
452
453 for( int i=0; i<50; i++ )
454 {
455 v3_copy( pco, pco1 );
456 apply_gravity( pv, pstep );
457 v3_muladds( pco, pv, pstep, pco );
458
459 //vg_line( pco, pco1, i&0x1?0xff000000:0xffffffff );
460
461 ray_hit contact;
462 v3f vdir;
463
464 v3_sub( pco, pco1, vdir );
465 contact.dist = v3_length( vdir );
466 v3_divs( vdir, contact.dist, vdir);
467
468 float orig_dist = contact.dist;
469 if( ray_world( pco1, vdir, &contact ))
470 {
471 v3f localup;
472 m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, localup );
473
474 float angle = v3_dot( localup, contact.normal );
475 v3f axis;
476 v3_cross( localup, contact.normal, axis );
477
478 time_to_impact += (contact.dist/orig_dist)*pstep;
479 limiter = vg_minf( 5.0f, time_to_impact )/5.0f;
480 limiter = 1.0f-limiter;
481 limiter *= limiter;
482 limiter = 1.0f-limiter;
483
484 if( angle < 0.99f )
485 {
486 v4f correction;
487 q_axis_angle( correction, axis, acosf(angle)*0.05f*(1.0f-limiter) );
488 q_mul( correction, player.rot, player.rot );
489 }
490
491 draw_cross( contact.pos, 0xffff0000, 1 );
492 break;
493 }
494 time_to_impact += pstep;
495 }
496
497 player.iY -= vg_get_axis( "horizontal" ) * 3.6f * ktimestep;
498 {
499
500 float iX = vg_get_axis( "vertical" ) * 3.6f * limiter * ktimestep;
501 static float siX = 0.0f;
502 siX = vg_lerpf( siX, iX, 0.3f );
503
504 v4f rotate;
505 v3f vside;
506
507 m3x3_mulv( player.to_world, (v3f){1.0f,0.0f,0.0f}, vside );
508
509 q_axis_angle( rotate, vside, siX );
510 q_mul( rotate, player.rot, player.rot );
511 }
512
513 v2f target = {0.0f,0.0f};
514 v2_muladds( target, (v2f){ vg_get_axis("h1"), vg_get_axis("v1") },
515 player.grab, target );
516 v2_lerp( player.board_xy, target, ktimestep*3.0f, player.board_xy );
517 }
518
519 static void player_do_motion(void)
520 {
521 float horizontal = vg_get_axis("horizontal"),
522 vertical = vg_get_axis("vertical");
523
524 player.joy_l[0] = vg_signf(horizontal) * powf( horizontal, 2.0f );
525 player.joy_l[1] = vg_signf(vertical) * powf( vertical, 2.0f );
526
527 if( player.in_air )
528 player_physics_air();
529
530 if( !player.in_air )
531 player_physics_ground();
532
533 /* Integrate velocity */
534 v3f prevco;
535 v3_copy( player.co, prevco );
536
537 apply_gravity( player.v, ktimestep );
538 v3_muladds( player.co, player.v, ktimestep, player.co );
539
540 /* Integrate inertia */
541 v4f rotate; v3f vup = {0.0f,1.0f,0.0f};
542 m3x3_mulv( player.to_world, vup, vup );
543
544 static float siY = 0.0f;
545
546 float lerpq = player.in_air? 0.04f: 0.3f;
547 siY = vg_lerpf( siY, player.iY, lerpq );
548
549 q_axis_angle( rotate, vup, siY );
550 q_mul( rotate, player.rot, player.rot );
551
552 player.iY = 0.0f; /* temp */
553
554 /* GATE COLLISION */
555
556 for( int i=0; i<world.gate_count; i++ )
557 {
558 teleport_gate *gate = &world.gates[i];
559
560 if( gate_intersect( gate, player.co, prevco ) )
561 {
562 m4x3_mulv( gate->transport, player.co, player.co );
563 m3x3_mulv( gate->transport, player.v, player.v );
564 m3x3_mulv( gate->transport, player.vl, player.vl );
565 m3x3_mulv( gate->transport, player.v_last, player.v_last );
566 m3x3_mulv( gate->transport, player.m, player.m );
567 m3x3_mulv( gate->transport, player.bob, player.bob );
568
569 v4f transport_rotation;
570 m3x3_q( gate->transport, transport_rotation );
571 q_mul( transport_rotation, player.rot, player.rot );
572
573 break;
574 }
575 }
576
577 /* Camera and character */
578 player_transform_update();
579
580 v3_lerp( player.vl, player.v, 0.05f, player.vl );
581
582 player.angles[0] = atan2f( player.vl[0], -player.vl[2] );
583 player.angles[1] = atan2f( -player.vl[1], sqrtf(player.vl[0]*player.vl[0]+
584 player.vl[2]*player.vl[2]) ) * 0.3f;
585 }
586
587 static int player_walkgrid_tri_walkable( u32 tri[3] )
588 {
589 return tri[0] < world.sm_road.vertex_count;
590 }
591
592 #define WALKGRID_SIZE 16
593 struct walkgrid
594 {
595 struct grid_sample
596 {
597 enum sample_type
598 {
599 k_sample_type_air, /* Nothing was hit. */
600 k_sample_type_invalid, /* The point is invalid, but there is a sample
601 underneath that can be used */
602 k_sample_type_valid, /* This point is good */
603 }
604 type;
605
606 v3f clip[2];
607 v3f pos;
608
609 enum traverse_state
610 {
611 k_traverse_none = 0x00,
612 k_traverse_h = 0x01,
613 k_traverse_v = 0x02
614 }
615 state;
616 }
617 samples[WALKGRID_SIZE][WALKGRID_SIZE];
618
619 #if 0
620 u32 geo[256];
621 #endif
622
623 boxf region;
624
625 float move; /* Current amount of movement we have left to apply */
626 v2f dir; /* The movement delta */
627 v2i cell_id;/* Current cell */
628 v2f pos; /* Local position (in cell) */
629 float h;
630 };
631
632 /*
633 * Get a sample at this pole location, will return 1 if the sample is valid,
634 * and pos will be updated to be the intersection location.
635 */
636 static void player_walkgrid_samplepole( struct grid_sample *s )
637 {
638 boxf region = {{ s->pos[0] -0.01f, s->pos[1] - 4.0f, s->pos[2] -0.01f},
639 { s->pos[0] +0.01f, s->pos[1] + 4.0f, s->pos[2] +0.01f}};
640
641 #if 0
642 vg_line( region[0],region[1], 0x20ffffff );
643 #endif
644
645 u32 geo[256];
646 v3f tri[3];
647 int len = bvh_select_triangles( &world.geo, region, geo, 256 );
648
649 const float k_minworld_y = -2000.0f;
650
651 float walk_height = k_minworld_y,
652 block_height = k_minworld_y;
653
654 s->type = k_sample_type_air;
655
656 for( int i=0; i<len; i++ )
657 {
658 u32 *ptri = &world.geo.indices[ geo[i] ];
659
660 for( int j=0; j<3; j++ )
661 v3_copy( world.geo.verts[ptri[j]].co, tri[j] );
662
663 v3f vdown = {0.0f,-1.0f,0.0f};
664 v3f sample_from;
665 v3_copy( s->pos, sample_from );
666 sample_from[1] = region[1][1];
667
668 float dist;
669 if( ray_tri( tri, sample_from, vdown, &dist ))
670 {
671 v3f p0;
672 v3_muladds( sample_from, vdown, dist, p0 );
673
674 if( player_walkgrid_tri_walkable(ptri) )
675 {
676 if( p0[1] > walk_height )
677 {
678 walk_height = p0[1];
679 }
680
681 #if 0
682 draw_cross( p0, 0xffffffff, 0.05f );
683 #endif
684 }
685 else
686 {
687 if( p0[1] > block_height )
688 block_height = p0[1];
689 #if 0
690 draw_cross( p0, 0xff0000ff, 0.05f );
691 #endif
692 }
693 }
694 }
695
696 s->pos[1] = walk_height;
697
698 if( walk_height > k_minworld_y )
699 if( block_height > walk_height )
700 s->type = k_sample_type_invalid;
701 else
702 s->type = k_sample_type_valid;
703 else
704 s->type = k_sample_type_air;
705
706 #if 0
707 if( s->type == k_sample_type_valid )
708 {
709 vg_line_pt3( s->pos, 0.01f, 0xff00ff00 );
710 }
711 #endif
712
713 #if 0
714 int count = 0;
715
716 ray_hit hit;
717 hit.dist = 10.0f;
718 count = bvh_raycast( &world.geo, sample_pos, vdir, &hit );
719
720 if( count )
721 {
722 v3_copy( hit.pos, s->pos );
723
724 if( !player_walkgrid_tri_walkable( hit.tri ) )
725 {
726 draw_cross( pos, 0xff0000ff, 0.05f );
727 return 0;
728 }
729 else
730 {
731 draw_cross( pos, 0xff00ff00, 0.05f );
732 return count;
733 }
734 }
735 else
736 return 0;
737 #endif
738 }
739
740 float const k_gridscale = 0.5f;
741
742 enum eclipdir
743 {
744 k_eclipdir_h = 0,
745 k_eclipdir_v = 1
746 };
747
748 static void player_walkgrid_clip_blocker( struct grid_sample *sa,
749 struct grid_sample *sb,
750 struct grid_sample *st,
751 enum eclipdir dir )
752 {
753 v3f clipdir, pos;
754 int valid_a = sa->type == k_sample_type_valid,
755 valid_b = sb->type == k_sample_type_valid;
756 struct grid_sample *target = valid_a? sa: sb,
757 *other = valid_a? sb: sa;
758 v3_copy( target->pos, pos );
759 v3_sub( other->pos, target->pos, clipdir );
760
761 boxf cell_region;
762 v3_muladds( pos, (v3f){1.0f,1.0f,1.0f}, -k_gridscale*2.1f, cell_region[0]);
763 v3_muladds( pos, (v3f){1.0f,1.0f,1.0f}, k_gridscale*2.1f, cell_region[1]);
764
765 u32 geo[256];
766 v3f tri[3];
767 int len = bvh_select_triangles( &world.geo, cell_region, geo, 256 );
768
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] ];
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 = bvh_select_triangles( &world.geo, 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] ];
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;
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 */