walk manifold clipping
[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
9 static struct gplayer
10 {
11 /* Physics */
12 v3f co, v, a, v_last, m, bob;
13 v4f rot;
14 float vswitch, slip, slip_last,
15 reverse;
16
17 float iY; /* Yaw inertia */
18 int in_air, is_dead, on_board;
19
20 /* Input */
21 v2f joy_l;
22
23 v2f board_xy;
24 float grab;
25 float pitch;
26
27 v3f land_target;
28 v3f land_target_log[22];
29 u32 land_target_colours[22];
30 int land_log_count;
31 m3x3f vr;
32
33 m4x3f to_world, to_local;
34
35 struct character mdl;
36
37 v3f handl_target, handr_target,
38 handl, handr;
39
40 /* Camera */
41 float air_blend;
42
43 v3f camera_pos, smooth_localcam;
44 v2f angles;
45 m4x3f camera, camera_inverse;
46 }
47 player;
48
49 static void player_transform_update(void)
50 {
51 q_normalize( player.rot );
52 q_m3x3( player.rot, player.to_world );
53 v3_copy( player.co, player.to_world[3] );
54
55 m4x3_invert_affine( player.to_world, player.to_local );
56 }
57
58 static int reset_player( int argc, char const *argv[] )
59 {
60 v3_zero( player.co );
61
62 if( argc == 1 )
63 {
64 if( !strcmp( argv[0], "tutorial" ))
65 v3_copy( world.tutorial, player.co );
66 }
67
68 v3_copy( (v3f){ 0.0f, 0.0f, -0.2f }, player.v );
69 q_identity( player.rot );
70 player.vswitch = 1.0f;
71 player.slip_last = 0.0f;
72 player.is_dead = 0;
73 player.in_air = 1;
74 m3x3_identity( player.vr );
75
76 player.mdl.shoes[0] = 1;
77 player.mdl.shoes[1] = 1;
78
79 player_transform_update();
80 return 0;
81 }
82
83 static void player_mouseview(void)
84 {
85 static v2f mouse_last,
86 view_vel = { 0.0f, 0.0f };
87
88 if( vg_get_button_down( "primary" ) )
89 v2_copy( vg_mouse, mouse_last );
90 else if( vg_get_button( "primary" ) )
91 {
92 v2f delta;
93 v2_sub( vg_mouse, mouse_last, delta );
94 v2_copy( vg_mouse, mouse_last );
95
96 v2_muladds( view_vel, delta, 0.005f, view_vel );
97 }
98
99 v2_muls( view_vel, 0.7f, view_vel );
100 v2_add( view_vel, player.angles, player.angles );
101 player.angles[1] = vg_clampf( player.angles[1], -VG_PIf*0.5f, VG_PIf*0.5f );
102
103 }
104
105 static void player_freecam(void)
106 {
107 player_mouseview();
108
109 float movespeed = 25.0f;
110 v3f lookdir = { 0.0f, 0.0f, -1.0f },
111 sidedir = { 1.0f, 0.0f, 0.0f };
112
113 m3x3_mulv( player.camera, lookdir, lookdir );
114 m3x3_mulv( player.camera, sidedir, sidedir );
115
116 static v3f move_vel = { 0.0f, 0.0f, 0.0f };
117 if( vg_get_button( "forward" ) )
118 v3_muladds( move_vel, lookdir, ktimestep * movespeed, move_vel );
119 if( vg_get_button( "back" ) )
120 v3_muladds( move_vel, lookdir, ktimestep *-movespeed, move_vel );
121 if( vg_get_button( "left" ) )
122 v3_muladds( move_vel, sidedir, ktimestep *-movespeed, move_vel );
123 if( vg_get_button( "right" ) )
124 v3_muladds( move_vel, sidedir, ktimestep * movespeed, move_vel );
125
126 v3_muls( move_vel, 0.7f, move_vel );
127 v3_add( move_vel, player.camera_pos, player.camera_pos );
128 }
129
130 static void apply_gravity( v3f vel, float const timestep )
131 {
132 v3f gravity = { 0.0f, -9.6f, 0.0f };
133 v3_muladds( vel, gravity, timestep, vel );
134 }
135
136 static void player_start_air(void)
137 {
138 player.in_air = 1;
139
140 float pstep = ktimestep*10.0f;
141
142 float best_velocity_mod = 0.0f,
143 best_velocity_delta = -9999.9f;
144
145 v3f axis, vup;
146 m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, vup );
147 v3_cross( vup, player.v, axis );
148 v3_normalize( axis );
149 player.land_log_count = 0;
150
151 m3x3_identity( player.vr );
152
153 for( int m=-3;m<=12; m++ )
154 {
155 float vmod = ((float)m / 15.0f)*0.09f;
156
157 v3f pco, pco1, pv;
158 v3_copy( player.co, pco );
159 v3_copy( player.v, pv );
160
161 /*
162 * Try different 'rotations' of the velocity to find the best possible
163 * landing normal. This conserves magnitude at the expense of slightly
164 * unrealistic results
165 */
166
167 m3x3f vr;
168 v4f vr_q;
169
170 q_axis_angle( vr_q, axis, vmod );
171 q_m3x3( vr_q, vr );
172
173 m3x3_mulv( vr, pv, pv );
174 v3_muladds( pco, pv, ktimestep, pco );
175
176 for( int i=0; i<50; i++ )
177 {
178 v3_copy( pco, pco1 );
179 apply_gravity( pv, pstep );
180
181 m3x3_mulv( vr, pv, pv );
182 v3_muladds( pco, pv, pstep, pco );
183
184 ray_hit contact;
185 v3f vdir;
186
187 v3_sub( pco, pco1, vdir );
188 contact.dist = v3_length( vdir );
189 v3_divs( vdir, contact.dist, vdir);
190
191 if( ray_world( pco1, vdir, &contact ))
192 {
193 float land_delta = v3_dot( pv, contact.normal );
194 u32 scolour = (u8)(vg_minf(-land_delta * 2.0f, 255.0f));
195
196 /* Bias prediction towords ramps */
197 if( ray_hit_is_ramp( &contact ) )
198 {
199 land_delta *= 0.1f;
200 scolour |= 0x0000a000;
201 }
202
203 if( (land_delta < 0.0f) && (land_delta > best_velocity_delta) )
204 {
205 best_velocity_delta = land_delta;
206 best_velocity_mod = vmod;
207
208 v3_copy( contact.pos, player.land_target );
209
210 q_axis_angle( vr_q, axis, vmod*0.1f );
211 q_m3x3( vr_q, player.vr );
212 }
213
214 v3_copy( contact.pos,
215 player.land_target_log[player.land_log_count] );
216 player.land_target_colours[player.land_log_count] =
217 0xff000000 | scolour;
218
219 player.land_log_count ++;
220
221 break;
222 }
223 }
224 }
225
226 //v3_rotate( player.v, best_velocity_mod, axis, player.v );
227
228 return;
229 v3_muls( player.v, best_velocity_mod, player.v );
230 }
231
232 static int sample_if_resistant( v3f pos )
233 {
234 v3f ground;
235 v3_copy( pos, ground );
236 ground[1] += 4.0f;
237
238 ray_hit hit;
239 hit.dist = INFINITY;
240
241 if( ray_world( ground, (v3f){0.0f,-1.0f,0.0f}, &hit ))
242 {
243 v3f angle;
244 v3_copy( player.v, angle );
245 v3_normalize( angle );
246 float resistance = v3_dot( hit.normal, angle );
247
248 if( resistance < 0.25f )
249 {
250 v3_copy( hit.pos, pos );
251 return 1;
252 }
253 }
254
255 return 0;
256 }
257
258 static float stable_force( float current, float diff )
259 {
260 float new = current + diff;
261
262 if( new * current < 0.0f )
263 return 0.0f;
264
265 return new;
266 }
267
268 static void player_physics_ground(void)
269 {
270 /*
271 * Getting surface collision points,
272 * the contact manifold is a triangle for simplicity.
273 */
274 v3f contact_front, contact_back, contact_norm, vup, vside,
275 axis;
276
277 float klength = 0.65f;
278 m4x3_mulv( player.to_world, (v3f){ 0.15f,0.0f,-klength}, contact_norm );
279 m4x3_mulv( player.to_world, (v3f){-0.15f,0.0f,-klength}, contact_front );
280 m4x3_mulv( player.to_world, (v3f){ 0.00f,0.0f, klength}, contact_back );
281 m3x3_mulv( player.to_world, (v3f){ 0.0f, 1.0f, 0.0f}, vup );
282 m3x3_mulv( player.to_world, (v3f){ 1.0f, 0.0f, 0.0f}, vside );
283
284 v3f cn0, cn1, cn2;
285
286 int contact_count =
287 sample_if_resistant( contact_front ) +
288 sample_if_resistant( contact_back ) +
289 sample_if_resistant( contact_norm );
290
291 if( contact_count < 3 )
292 {
293 player_start_air();
294 return;
295 }
296
297 v3f norm;
298 v3f v0, v1;
299 v3_sub( contact_norm, contact_front, v0 );
300 v3_sub( contact_back, contact_front, v1 );
301 v3_cross( v1, v0, norm );
302 v3_normalize( norm );
303
304 vg_line( contact_norm, contact_front, 0xff00ff00 );
305 vg_line( contact_back, contact_front, 0xff0000ff );
306
307 /* Surface alignment */
308 float angle = v3_dot( vup, norm );
309 v3_cross( vup, norm, axis );
310
311 if( angle < 0.999f )
312 {
313 v4f correction;
314 q_axis_angle( correction, axis, acosf(angle) );
315 q_mul( correction, player.rot, player.rot );
316 }
317
318 float resistance = v3_dot( norm, player.v );
319 if( resistance >= 0.0f )
320 {
321 player_start_air();
322 return;
323 }
324 else
325 {
326 v3_muladds( player.v, norm, -resistance, player.v );
327 }
328
329 /* This is where velocity integration used to be */
330
331 float slip = 0.0f;
332
333 player.co[1] = (contact_front[1]+contact_back[1])*0.5f;
334
335 v3f vel;
336 m3x3_mulv( player.to_local, player.v, vel );
337
338 /* Calculate local forces */
339
340 if( fabsf(vel[2]) > 0.01f )
341 slip = fabsf(-vel[0] / vel[2]) * vg_signf(vel[0]);
342
343 if( fabsf( slip ) > 1.2f )
344 slip = vg_signf( slip ) * 1.2f;
345 player.slip = slip;
346 player.reverse = -vg_signf(vel[2]);
347
348 float substep = ktimestep * 0.2f;
349 float fwd_resistance = (vg_get_button( "break" )? 5.0f: 0.02f) * -substep;
350
351 for( int i=0; i<5; i++ )
352 {
353 vel[2] = stable_force( vel[2], vg_signf( vel[2] ) * fwd_resistance );
354 vel[0] = stable_force( vel[0], vg_signf( vel[0] ) * -7.0f *substep );
355 }
356
357 static double start_push = 0.0;
358 if( vg_get_button_down( "push" ) )
359 start_push = vg_time;
360
361 if( !vg_get_button("break") && vg_get_button( "push" ) )
362 {
363 float const k_maxpush = 16.0f,
364 k_pushaccel = 5.0f;
365
366 float cycle_time = vg_time-start_push,
367 amt = k_pushaccel * (sinf( cycle_time * 8.0f )*0.5f+0.5f)*ktimestep,
368 current = v3_length( vel ),
369 new_vel = vg_minf( current + amt, k_maxpush );
370 new_vel -= vg_minf(current, k_maxpush);
371 vel[2] -= new_vel * player.reverse;
372 }
373
374 m3x3_mulv( player.to_world, vel, player.v );
375
376 if( vg_get_button( "yawl" ) )
377 player.iY += 3.6f * ktimestep;
378 if( vg_get_button( "yawr" ) )
379 player.iY -= 3.6f * ktimestep;
380
381 float steer = vg_get_axis( "horizontal" );
382 player.iY -= vg_signf(steer)*powf(steer,2.0f) * 1.5f * ktimestep;
383
384 /* Too much lean and it starts to look like a snowboard here */
385 v2_lerp( player.board_xy, (v2f){ slip*0.25f, 0.0f },
386 ktimestep*5.0f, player.board_xy);
387 }
388
389 static void draw_cross(v3f pos,u32 colour, float scale)
390 {
391 v3f p0, p1;
392 v3_add( (v3f){ scale,0.0f,0.0f}, pos, p0 );
393 v3_add( (v3f){-scale,0.0f,0.0f}, pos, p1 );
394 vg_line( p0, p1, colour );
395 v3_add( (v3f){0.0f, scale,0.0f}, pos, p0 );
396 v3_add( (v3f){0.0f,-scale,0.0f}, pos, p1 );
397 vg_line( p0, p1, colour );
398 v3_add( (v3f){0.0f,0.0f, scale}, pos, p0 );
399 v3_add( (v3f){0.0f,0.0f,-scale}, pos, p1 );
400 vg_line( p0, p1, colour );
401 }
402
403 static void player_physics_air(void)
404 {
405 m3x3_mulv( player.vr, player.v, player.v );
406 for( int i=0; i<player.land_log_count; i++ )
407 draw_cross( player.land_target_log[i], player.land_target_colours[i], 1);
408
409 draw_cross( player.land_target, 0xff0000ff, 1 );
410
411 v3f ground_pos;
412 v3_copy( player.co, ground_pos );
413 ground_pos[1] += 4.0f;
414
415 ray_hit hit;
416 hit.dist = INFINITY;
417 if( ray_world( ground_pos, (v3f){0.0f,-1.0f,0.0f}, &hit ))
418 {
419 if( hit.pos[1] > player.co[1] )
420 {
421 player.in_air = 0;
422
423 if( !ray_hit_is_ramp( &hit ) )
424 {
425 player.is_dead = 1;
426 character_ragdoll_copypose( &player.mdl, player.v );
427 }
428
429 return;
430 }
431 }
432
433 /* Prediction
434 *
435 * TODO: Find best landing surface and guide player towords it
436 */
437 float pstep = ktimestep*10.0f;
438
439 v3f pco, pco1, pv;
440 v3_copy( player.co, pco );
441 v3_copy( player.v, pv );
442
443 float time_to_impact = 0.0f;
444 float limiter = 1.0f;
445
446 for( int i=0; i<50; i++ )
447 {
448 v3_copy( pco, pco1 );
449 apply_gravity( pv, pstep );
450 v3_muladds( pco, pv, pstep, pco );
451
452 //vg_line( pco, pco1, i&0x1?0xff000000:0xffffffff );
453
454 ray_hit contact;
455 v3f vdir;
456
457 v3_sub( pco, pco1, vdir );
458 contact.dist = v3_length( vdir );
459 v3_divs( vdir, contact.dist, vdir);
460
461 float orig_dist = contact.dist;
462 if( ray_world( pco1, vdir, &contact ))
463 {
464 v3f localup;
465 m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, localup );
466
467 float angle = v3_dot( localup, contact.normal );
468 v3f axis;
469 v3_cross( localup, contact.normal, axis );
470
471 time_to_impact += (contact.dist/orig_dist)*pstep;
472 limiter = vg_minf( 5.0f, time_to_impact )/5.0f;
473 limiter = 1.0f-limiter;
474 limiter *= limiter;
475 limiter = 1.0f-limiter;
476
477 if( angle < 0.99f )
478 {
479 v4f correction;
480 q_axis_angle( correction, axis, acosf(angle)*0.05f*(1.0f-limiter) );
481 q_mul( correction, player.rot, player.rot );
482 }
483
484 draw_cross( contact.pos, 0xffff0000, 1 );
485 break;
486 }
487 time_to_impact += pstep;
488 }
489
490 player.iY -= vg_get_axis( "horizontal" ) * 3.6f * ktimestep;
491 {
492
493 float iX = vg_get_axis( "vertical" ) * 3.6f * limiter * ktimestep;
494 static float siX = 0.0f;
495 siX = vg_lerpf( siX, iX, 0.3f );
496
497 v4f rotate;
498 v3f vside;
499
500 m3x3_mulv( player.to_world, (v3f){1.0f,0.0f,0.0f}, vside );
501
502 q_axis_angle( rotate, vside, siX );
503 q_mul( rotate, player.rot, player.rot );
504 }
505
506 v2f target = {0.0f,0.0f};
507 v2_muladds( target, (v2f){ vg_get_axis("h1"), vg_get_axis("v1") },
508 player.grab, target );
509 v2_lerp( player.board_xy, target, ktimestep*3.0f, player.board_xy );
510 }
511
512 static void player_do_motion(void)
513 {
514 float horizontal = vg_get_axis("horizontal"),
515 vertical = vg_get_axis("vertical");
516
517 player.joy_l[0] = vg_signf(horizontal) * powf( horizontal, 2.0f );
518 player.joy_l[1] = vg_signf(vertical) * powf( vertical, 2.0f );
519
520 if( player.in_air )
521 player_physics_air();
522
523 if( !player.in_air )
524 player_physics_ground();
525
526 /* Integrate velocity */
527 v3f prevco;
528 v3_copy( player.co, prevco );
529
530 apply_gravity( player.v, ktimestep );
531 v3_muladds( player.co, player.v, ktimestep, player.co );
532
533 /* Integrate inertia */
534 v4f rotate; v3f vup = {0.0f,1.0f,0.0f};
535 m3x3_mulv( player.to_world, vup, vup );
536
537 static float siY = 0.0f;
538
539 float lerpq = player.in_air? 0.04f: 0.3f;
540 siY = vg_lerpf( siY, player.iY, lerpq );
541
542 q_axis_angle( rotate, vup, siY );
543 q_mul( rotate, player.rot, player.rot );
544
545 player.iY = 0.0f; /* temp */
546
547 #if 0
548 /* GATE COLLISION */
549 if( gate_intersect( &gate_a, player.co, prevco ) )
550 {
551 teleport_gate *gate = &gate_a;
552
553 m4x3f transport;
554 m4x3_mul( gate->other->to_world, gate->to_local, transport );
555 m4x3_mulv( transport, player.co, player.co );
556 m3x3_mulv( transport, player.v, player.v );
557 m3x3_mulv( transport, player.v_last, player.v_last );
558 m3x3_mulv( transport, player.m, player.m );
559 m3x3_mulv( transport, player.bob, player.bob );
560
561 v4f transport_rotation;
562 m3x3_q( transport, transport_rotation );
563 q_mul( transport_rotation, player.rot, player.rot );
564 }
565 #endif
566
567 /* Camera and character */
568 player_transform_update();
569
570 player.angles[0] = atan2f( player.v[0], -player.v[2] );
571 player.angles[1] = atan2f( -player.v[1], sqrtf(player.v[0]*player.v[0]+
572 player.v[2]*player.v[2]) ) * 0.3f;
573
574 player.air_blend = vg_lerpf( player.air_blend, player.in_air, 0.04f );
575 v3_muladds( player.camera_pos, player.v, -0.05f*player.air_blend,
576 player.camera_pos );
577 }
578
579 /*
580 * Get a sample at this pole location, will return 1 if the sample is valid,
581 * and pos will be updated to be the intersection location.
582 */
583 static int player_walkgrid_samplepole( u32 *geo, int len, v3f pos )
584 {
585 v3f p1;
586 v3_copy( pos, p1 );
587 p1[1] -= 10.0f;
588
589 vg_line( pos, p1, 0x20ffffff );
590
591 v3f sample_pos;
592 v3_copy(pos, sample_pos);
593
594 v3f vdir = {0.0f,-1.0f,0.0f};
595 int count = 0;
596
597 ray_hit hit;
598 hit.dist = INFINITY;
599 for( int i=0; i<len; i++ )
600 {
601 u32 *tri = &world.geo.indices[ geo[i] ];
602 count += bvh_ray_tri( &world.geo, tri, sample_pos, vdir, &hit );
603 }
604
605 if( count )
606 {
607 v3f v0, v1;
608 float *pa = world.geo.verts[hit.tri[0]].co,
609 *pb = world.geo.verts[hit.tri[1]].co,
610 *pc = world.geo.verts[hit.tri[2]].co;
611
612 v3_sub( pa, pb, v0 );
613 v3_sub( pc, pb, v1 );
614 v3_cross( v1, v0, hit.normal );
615 v3_normalize( hit.normal );
616 v3_muladds( sample_pos, vdir, hit.dist, pos );
617
618 draw_cross( pos, 0xff00ff00, 0.05f );
619 return count;
620 }
621 else
622 return 0;
623 }
624
625 static void player_walkgrid_clip(u32 *geo, int len, v3f pos, v3f dir, v3f clip)
626 {
627 float max_dist = 0.0f;
628 v3f tri[3];
629 v3f perp;
630 v3_cross( dir,(v3f){0.0f,1.0f,0.0f},perp );
631 v3_copy( pos, clip );
632
633 for( int i=0; i<len; i++ )
634 {
635 u32 *ptri = &world.geo.indices[ geo[i] ];
636 for( int j=0; j<3; j++ )
637 v3_copy( world.geo.verts[ptri[j]].co, tri[j] );
638
639 for( int k=0; k<3; k++ )
640 {
641 int ia = k,
642 ib = (k+1)%3;
643
644 v3f v0, v1;
645 v3_sub( tri[ia], pos, v0 );
646 v3_sub( tri[ib], pos, v1 );
647
648 if( (dir[2]*v0[0] - dir[0]*v0[2]) *
649 (dir[2]*v1[0] - dir[0]*v1[2]) < 0.0f )
650 {
651 float da = v3_dot(v0,perp),
652 db = v3_dot(v1,perp),
653 d = da-db,
654 qa = da/d;
655
656 v3f p0;
657 v3_muls( v1, qa, p0 );
658 v3_muladds( p0, v0, 1.0f-qa, p0 );
659
660 float h = v3_dot(p0,dir)/v3_dot(dir,dir);
661
662 if( h >= max_dist && h <= 1.0f )
663 {
664 max_dist = h;
665 v3_copy( p0, clip );
666 }
667 }
668 }
669 }
670
671 v3f clippos;
672 v3_add( pos, clip, clippos );
673 draw_cross( clippos, 0xffffff00, 0.05f );
674 }
675
676 static void player_walkgrid_getsurface(void)
677 {
678 float const k_gridscale = 0.5f;
679 float const k_stepheight = 0.5f;
680 float const k_walkspeed = 6.0f;
681 float const k_miny = 0.6f;
682 float const k_height = 1.78f;
683 int const k_gridamt = 8;
684 float const k_region_size = (float)k_gridamt/2.0f * k_gridscale;
685
686 v3f cell;
687 v3_muls( player.co, 1.0f/k_gridscale, cell );
688 v3_floor( cell, cell );
689 v3_muls( cell, k_gridscale, cell );
690
691 u32 geo[128];
692
693 boxf region;
694 v3_muladds( cell, (v3f){-1.0f,-1.0f,-1.0f}, k_region_size, region[0] );
695 v3_muladds( cell, (v3f){ 1.0f, 1.0f, 1.0f}, k_region_size, region[1] );
696
697 int tri_count = bvh_select_triangles( &world.geo, region, geo, 128 );
698
699 v3f tri[3];
700 for( int i=0; i<tri_count; i++ )
701 {
702 for( int j=0; j<3; j++ )
703 v3_copy( world.geo.verts[ world.geo.indices[geo[i]+j] ].co, tri[j] );
704
705 #if 0
706 vg_line( tri[0], tri[1], 0xffa2ff30 );
707 vg_line( tri[1], tri[2], 0xffa2ff30 );
708 vg_line( tri[2], tri[0], 0xffa2ff30 );
709 #endif
710 }
711
712 struct grid_sample
713 {
714 int valid;
715 v3f clip[2];
716 v3f pos;
717 }
718 samples[ k_gridamt ][ k_gridamt ];
719
720 /* Get surface samples */
721 for( int y=0; y<k_gridamt; y++ )
722 {
723 for( int x=0; x<k_gridamt; x++ )
724 {
725 struct grid_sample *s = &samples[y][x];
726 v3_muladds( region[0], (v3f){ x, 0, y }, k_gridscale, s->pos );
727 s->pos[1] = player.co[1] + k_height;
728
729 s->valid = player_walkgrid_samplepole( geo, tri_count, s->pos )? 1: 0;
730 }
731 }
732
733 /*
734 * Calculate h+v clipping distances.
735 * Distances are stored in A always, so you know that if the sample is
736 * invalid, this signifies the start of the manifold as opposed to the
737 * extent or bounds of it.
738 */
739 for( int i=0; i<2; i++ )
740 {
741 for( int x=0; x<k_gridamt; x++ )
742 {
743 for( int z=0; z<k_gridamt-1; z++ )
744 {
745 v3f clipdir = { 0.0f, 0.0f, 0.0f };
746
747 struct grid_sample *sa, *sb;
748 if( i == 1 )
749 {
750 sa = &samples[z][x];
751 sb = &samples[z+1][x];
752 }
753 else
754 {
755 sa = &samples[x][z];
756 sb = &samples[x][z+1];
757 }
758
759 if( sa->valid != sb->valid )
760 {
761 clipdir[i*2] = (float)(sa->valid - sb->valid)*k_gridscale;
762 player_walkgrid_clip( geo, tri_count,
763 sa->valid? sa->pos: sb->pos,
764 clipdir, sa->clip[i] );
765 }
766 else
767 {
768 if( sa->valid )
769 {
770 vg_line( sa->pos, sb->pos, 0xffffffff );
771 }
772 }
773 }
774 }
775 }
776
777 /* Draw connections */
778 for( int x=0; x<k_gridamt-1; x++ )
779 {
780 for( int z=0; z<k_gridamt-1; z++ )
781 {
782 static const struct conf
783 {
784 struct confedge
785 {
786 /* i: sample index
787 * d: data index
788 * a: axis index
789 */
790 int i0, i1,
791 d0, d1,
792 a0, a1;
793 }
794 edges[2];
795 int edge_count;
796 }
797 k_configs[16] = {
798 {{},0},
799 {{{ 3, 3, 3, 0, 1,0 }}, 1},
800 {{{ 2, 2, 1, 3, 0,1 }}, 1},
801 {{{ 2, 3, 1, 0, 0,0 }}, 1},
802
803 {{{ 1, 1, 0, 1, 1,0 }}, 1},
804 {{{ 3, 3, 3, 0, 1,0 },
805 { 1, 1, 0, 1, 1,0 }}, 2},
806 {{{ 1, 2, 0, 3, 1,1 }}, 1},
807 {{{ 1, 3, 0, 0, 1,0 }}, 1},
808
809 {{{ 0, 0, 0, 0, 0,1 }}, 1},
810 {{{ 3, 0, 3, 0, 1,1 }}, 1},
811 {{{ 2, 2, 1, 3, 0,1 },
812 { 0, 0, 0, 0, 0,1 }}, 2},
813 {{{ 2, 0, 1, 0, 0,1 }}, 1},
814
815 {{{ 0, 1, 0, 1, 0,0 }}, 1},
816 {{{ 3, 1, 3, 1, 1,0 }}, 1},
817 {{{ 0, 2, 0, 3, 0,1 }}, 1},
818 {{},0},
819 };
820
821 struct grid_sample *corners[4] =
822 {
823 &samples[z][x],
824 &samples[z+1][x],
825 &samples[z+1][x+1],
826 &samples[z][x+1]
827 };
828
829 u32 config = (corners[0]->valid<<3) | (corners[1]->valid<<2) |
830 (corners[2]->valid<<1) | corners[3]->valid;
831
832 const struct conf *conf = &k_configs[ config ];
833
834 for( int i=0; i<conf->edge_count; i++ )
835 {
836 const struct confedge *edge = &conf->edges[i];
837
838 v3f p0, p1;
839 v3_add( corners[edge->i0]->pos,
840 corners[edge->d0]->clip[edge->a0], p0 );
841 v3_add( corners[edge->i1]->pos,
842 corners[edge->d1]->clip[edge->a1], p1 );
843 vg_line( p0, p1, 0xff0000ff );
844
845 vg_line( corners[edge->i0]->pos, p0, 0xffffffff );
846 vg_line( corners[edge->i1]->pos, p1, 0xffffffff );
847 }
848 }
849 }
850 }
851
852 static void player_walkgrid(void)
853 {
854 player_walkgrid_getsurface();
855
856 float const k_gridscale = 0.5f;
857 float const k_stepheight = 0.5f;
858 float const k_walkspeed = 6.0f;
859 float const k_miny = 0.6f;
860 float const k_height = 1.78f;
861 int const k_gridamt = 8;
862
863 #if 0
864 v3f cell;
865 v3_muls( player.co, 1.0f/k_gridscale, cell );
866 v3_floor( cell, cell );
867 v3_muls( cell, k_gridscale, cell );
868
869 struct grid_sample
870 {
871 ray_hit hit;
872 int valid;
873 }
874 samples[ k_gridamt ][ k_gridamt ];
875
876 v3f grid_origin;
877 v3_muladds( cell, (v3f){ -1.0f,0.0f,-1.0f },
878 (float)(k_gridamt/2) * k_gridscale, grid_origin );
879
880 /*
881 * Get sample 'poles'
882 */
883 for( int y=0; y<k_gridamt; y++ )
884 {
885 for( int x=0; x<k_gridamt; x++ )
886 {
887 v3f sample_coord;
888 v3_muladds( grid_origin, (v3f){ x, 0, y }, k_gridscale, sample_coord );
889 sample_coord[1] += k_height;
890
891 struct grid_sample *sample = &samples[y][x];
892 sample->valid = 0;
893 sample->hit.dist = k_stepheight+k_height;
894
895 if( ray_world( sample_coord, (v3f){0.0f,-1.0f,0.0f}, &sample->hit ))
896 {
897 if( sample->hit.normal[1] >= k_miny &&
898 ray_hit_is_ramp( &sample->hit ))
899 {
900 sample->valid = 1;
901 draw_cross( sample->hit.pos, 0xff00ff00, 0.1f );
902 }
903 else
904 draw_cross( sample->hit.pos, 0xff0000ff, 0.1f );
905 }
906 }
907 }
908
909 /*
910 * Clip grid intersections with triangle edges
911 */
912 for( int dir=0; dir<2; dir++ )
913 {
914 for( int x=0; x<k_gridamt; x++ )
915 {
916 for( int y=0; y<k_gridamt-1; y++ )
917 {
918 struct grid_sample *sa, *sb;
919
920 if( dir == 0 )
921 {
922 sa = &samples[y][x];
923 sb = &samples[y+1][x];
924 }
925 else
926 {
927 sa = &samples[x][y];
928 sb = &samples[x][y+1];
929 }
930
931 if( (sa->valid != sb->valid) && (sa->valid||sb->valid) )
932 {
933 int line = dir==0? 0:2,
934 axis = dir==0? 2:0;
935
936 v3f tri[3];
937 ray_world_get_tri( sa->valid? &sa->hit: &sb->hit, tri );
938
939 v3f other = {0,0,0};
940 other[axis] = sa->valid? k_gridscale: -k_gridscale;
941 v3_add( sa->valid? sa->hit.pos: sb->hit.pos, other, other );
942 vg_line( sa->valid? sa->hit.pos: sb->hit.pos,
943 other, 0xffffffff );
944
945 v3f sample;
946 if( dir == 0 )
947 v3_muladds( grid_origin, (v3f){ x, 0, y }, k_gridscale, sample);
948 else
949 v3_muladds( grid_origin, (v3f){ y, 0, x }, k_gridscale, sample);
950
951 /* Clip triangles until we find an edge inside the cell */
952 float offset = sample[line],
953 basis = sample[axis];
954
955 for( int i=0; i<3; i++ )
956 {
957 int ia = i,
958 ib = (i+1)%3;
959 float pa = tri[ia][line],
960 pb = tri[ib][line];
961
962 vg_line( tri[ia],tri[ib],0xffaaaaaa );
963
964 if( (pa-offset)*(pb-offset) > 0.0f )
965 continue;
966
967 float d = pb-pa,
968 qa = (offset-pa)/d,
969 h = qa*tri[ib][axis] + (1.0f-qa)*tri[ia][axis],
970 q = (h-basis)/k_gridscale;
971
972 if( q >= 0.0f && q <= 1.0f )
973 {
974 float height = qa*tri[ia][1] + (1.0f-qa)*tri[ib][1];
975 v3f intersection;
976 if( dir == 0 )
977 v3_copy( (v3f){ offset, height, h }, intersection );
978 else
979 v3_copy( (v3f){ h, height, offset }, intersection );
980 draw_cross( intersection, 0xffff0000, 0.06f );
981 break;
982 }
983 }
984 }
985 }
986 }
987 }
988 #endif
989 v3f fwd = { -sinf(-player.angles[0]), 0.0f, -cosf(-player.angles[0]) },
990 side = { -fwd[2], 0.0f, fwd[0] };
991
992 /* Temp */
993 if( glfwGetKey( vg_window, GLFW_KEY_W ) )
994 v3_muladds( player.co, fwd, ktimestep*k_walkspeed, player.co );
995 if( glfwGetKey( vg_window, GLFW_KEY_S ) )
996 v3_muladds( player.co, fwd, -ktimestep*k_walkspeed, player.co );
997
998 if( glfwGetKey( vg_window, GLFW_KEY_A ) )
999 v3_muladds( player.co, side, -ktimestep*k_walkspeed, player.co );
1000 if( glfwGetKey( vg_window, GLFW_KEY_D ) )
1001 v3_muladds( player.co, side, ktimestep*k_walkspeed, player.co );
1002
1003 m4x3_mulv( player.to_world, (v3f){0.0f,1.8f,0.0f}, player.camera_pos );
1004 player_mouseview();
1005 player_transform_update();
1006 }
1007
1008 static void player_animate(void)
1009 {
1010 /* Camera position */
1011 v3_sub( player.v, player.v_last, player.a );
1012 v3_copy( player.v, player.v_last );
1013
1014 v3_add( player.m, player.a, player.m );
1015 v3_lerp( player.m, (v3f){0.0f,0.0f,0.0f}, 0.1f, player.m );
1016 v3f target;
1017
1018 player.m[0] = vg_clampf( player.m[0], -2.0f, 2.0f );
1019 player.m[1] = vg_clampf( player.m[1], -0.2f, 5.0f );
1020 player.m[2] = vg_clampf( player.m[2], -2.0f, 2.0f );
1021 v3_copy( player.m, target );
1022 v3_lerp( player.bob, target, 0.2f, player.bob );
1023
1024 /* Head */
1025 float lslip = fabsf(player.slip); //vg_minf( 0.4f, slip );
1026
1027 float grabt = vg_get_axis( "grabr" )*0.5f+0.5f;
1028 player.grab = vg_lerpf( player.grab, grabt, 0.04f );
1029
1030 float kheight = 2.0f,
1031 kleg = 0.6f;
1032
1033 v3f head;
1034 head[0] = 0.0f;
1035 head[1] = (0.3f+cosf(lslip)*0.5f*(1.0f-player.grab*0.7f)) * kheight;
1036 head[2] = 0.0f;
1037
1038 v3f offset;
1039 m3x3_mulv( player.to_local, player.bob, offset );
1040
1041 offset[0] *= 0.3333f;
1042 offset[1] *= -0.25f;
1043 offset[2] *= 0.7f;
1044 v3_muladds( head, offset, 0.7f, head );
1045 head[1] = vg_clampf( head[1], 0.3f, kheight );
1046
1047 #if 0
1048 if( !freecam )
1049 {
1050 v3_copy( head, player.view );
1051 v3f camoffs = {-0.2f,-0.6f,0.00f};
1052 v3_add( player.view, camoffs, player.view );
1053 }
1054 #endif
1055
1056 /*
1057 * Animation blending
1058 * ===========================================
1059 */
1060
1061 static float fslide = 0.0f;
1062 static float fdirz = 0.0f;
1063 static float fdirx = 0.0f;
1064 static float fstand = 0.0f;
1065 static float ffly = 0.0f;
1066
1067 float speed = v3_length( player.v );
1068
1069 fstand = vg_lerpf(fstand, 1.0f-vg_clampf(speed*0.03f,0.0f,1.0f),0.1f);
1070 fslide = vg_lerpf(fslide, vg_clampf(lslip+fabsf(offset[0])*0.2f,
1071 0.0f,1.0f), 0.04f);
1072 fdirz = vg_lerpf(fdirz, player.reverse > 0.0f? 1.0f: 0.0f, 0.04f );
1073 fdirx = vg_lerpf(fdirx, player.slip < 0.0f? 1.0f: 0.0f, 0.04f );
1074 ffly = vg_lerpf(ffly, player.in_air? 1.0f: 0.0f, 0.04f );
1075
1076 character_pose_reset( &player.mdl );
1077
1078 float amt_air = ffly*ffly,
1079 amt_ground = 1.0f-amt_air,
1080 amt_std = (1.0f-fslide) * amt_ground,
1081 amt_stand = amt_std * fstand,
1082 amt_aero = amt_std * (1.0f-fstand),
1083 amt_slide = amt_ground * fslide;
1084
1085 character_final_pose( &player.mdl, offset, &pose_stand, amt_stand );
1086 character_final_pose( &player.mdl, offset, &pose_aero, amt_aero*fdirz );
1087 character_final_pose( &player.mdl, offset,
1088 &pose_aero_reverse, amt_aero * (1.0f-fdirz) );
1089 character_final_pose( &player.mdl, offset, &pose_slide, amt_slide*fdirx );
1090 character_final_pose( &player.mdl, offset,
1091 &pose_slide1, amt_slide*(1.0f-fdirx) );
1092
1093 character_final_pose( &player.mdl, (v3f){0.0f,0.0f,0.0f},
1094 &pose_fly, amt_air );
1095
1096 /* Camera position */
1097 v3_lerp( player.smooth_localcam, player.mdl.cam_pos, 0.08f,
1098 player.smooth_localcam );
1099 v3_muladds( player.smooth_localcam, offset, 0.7f, player.camera_pos );
1100 player.camera_pos[1] = vg_clampf( player.camera_pos[1], 0.3f, kheight );
1101 m4x3_mulv( player.to_world, player.camera_pos, player.camera_pos );
1102
1103 /*
1104 * Additive effects
1105 * ==========================
1106 */
1107 struct ik_basic *arm_l = &player.mdl.ik_arm_l,
1108 *arm_r = &player.mdl.ik_arm_r;
1109
1110 v3f localv;
1111 m3x3_mulv( player.to_local, player.v, localv );
1112 v3_muladds( arm_l->end, localv, -0.01f, arm_l->end );
1113 v3_muladds( arm_r->end, localv, -0.01f, arm_r->end );
1114
1115 /* New board transformation */
1116 v4f board_rotation; v3f board_location;
1117
1118 v4f rz, rx;
1119 q_axis_angle( rz, (v3f){ 0.0f, 0.0f, 1.0f }, player.board_xy[0] );
1120 q_axis_angle( rx, (v3f){ 1.0f, 0.0f, 0.0f }, player.board_xy[1] );
1121 q_mul( rx, rz, board_rotation );
1122
1123 v3f *mboard = player.mdl.matrices[k_chpart_board];// player.mboard;
1124 q_m3x3( board_rotation, mboard );
1125 m3x3_mulv( mboard, (v3f){ 0.0f, -0.5f, 0.0f }, board_location );
1126 v3_add( (v3f){0.0f,0.5f,0.0f}, board_location, board_location );
1127 v3_copy( board_location, mboard[3] );
1128
1129
1130 float wheel_r = offset[0]*-0.4f;
1131 v4f qwheel;
1132 q_axis_angle( qwheel, (v3f){0.0f,1.0f,0.0f}, wheel_r );
1133
1134 q_m3x3( qwheel, player.mdl.matrices[k_chpart_wb] );
1135
1136 m3x3_transpose( player.mdl.matrices[k_chpart_wb],
1137 player.mdl.matrices[k_chpart_wf] );
1138 v3_copy( player.mdl.offsets[k_chpart_wb],
1139 player.mdl.matrices[k_chpart_wb][3] );
1140 v3_copy( player.mdl.offsets[k_chpart_wf],
1141 player.mdl.matrices[k_chpart_wf][3] );
1142
1143 m4x3_mul( mboard, player.mdl.matrices[k_chpart_wb],
1144 player.mdl.matrices[k_chpart_wb] );
1145 m4x3_mul( mboard, player.mdl.matrices[k_chpart_wf],
1146 player.mdl.matrices[k_chpart_wf] );
1147
1148 m4x3_mulv( mboard, player.mdl.ik_leg_l.end, player.mdl.ik_leg_l.end );
1149 m4x3_mulv( mboard, player.mdl.ik_leg_r.end, player.mdl.ik_leg_r.end );
1150
1151
1152 v3_copy( player.mdl.ik_arm_l.end, player.handl_target );
1153 v3_copy( player.mdl.ik_arm_r.end, player.handr_target );
1154
1155 if( 1||player.in_air )
1156 {
1157 float tuck = player.board_xy[1],
1158 tuck_amt = fabsf( tuck ) * (1.0f-fabsf(player.board_xy[0]));
1159
1160 float crouch = player.grab*0.3f;
1161 v3_muladds( player.mdl.ik_body.base, (v3f){0.0f,-1.0f,0.0f},
1162 crouch, player.mdl.ik_body.base );
1163 v3_muladds( player.mdl.ik_body.end, (v3f){0.0f,-1.0f,0.0f},
1164 crouch*1.2f, player.mdl.ik_body.end );
1165
1166 if( tuck < 0.0f )
1167 {
1168 //foot_l *= 1.0f-tuck_amt*1.5f;
1169
1170 if( player.grab > 0.1f )
1171 {
1172 m4x3_mulv( mboard, (v3f){0.1f,0.14f,0.6f},
1173 player.handl_target );
1174 }
1175 }
1176 else
1177 {
1178 //foot_r *= 1.0f-tuck_amt*1.4f;
1179
1180 if( player.grab > 0.1f )
1181 {
1182 m4x3_mulv( mboard, (v3f){0.1f,0.14f,-0.6f},
1183 player.handr_target );
1184 }
1185 }
1186 }
1187
1188 v3_lerp( player.handl, player.handl_target, 0.1f, player.handl );
1189 v3_lerp( player.handr, player.handr_target, 0.1f, player.handr );
1190
1191 v3_copy( player.handl, player.mdl.ik_arm_l.end );
1192 v3_copy( player.handr, player.mdl.ik_arm_r.end );
1193
1194 /* Head rotation */
1195
1196 static float rhead = 0.0f;
1197 rhead = vg_lerpf( rhead,
1198 vg_clampf(atan2f( localv[2], -localv[0] ),-1.0f,1.0f), 0.04f );
1199 player.mdl.rhead = rhead;
1200 }
1201
1202 static void player_update(void)
1203 {
1204 if( vg_get_axis("grabl")>0.0f)
1205 reset_player(0,NULL);
1206
1207 if( freecam )
1208 {
1209 player_freecam();
1210 }
1211 else
1212 {
1213 if( player.is_dead )
1214 {
1215 character_ragdoll_iter( &player.mdl );
1216 character_debug_ragdoll( &player.mdl );
1217 }
1218 else
1219 {
1220 if( player.on_board )
1221 {
1222 player_do_motion();
1223 player_animate();
1224 }
1225 else
1226 {
1227 player_walkgrid();
1228 }
1229 }
1230 }
1231
1232 /* Update camera matrices */
1233 m4x3_identity( player.camera );
1234 m4x3_rotate_y( player.camera, -player.angles[0] );
1235 m4x3_rotate_x( player.camera, -0.33f -player.angles[1] );
1236 v3_copy( player.camera_pos, player.camera[3] );
1237 m4x3_invert_affine( player.camera, player.camera_inverse );
1238 }
1239
1240 static void draw_player(void)
1241 {
1242 /* Draw */
1243 m4x3_copy( player.to_world, player.mdl.mroot );
1244
1245 if( player.is_dead )
1246 character_mimic_ragdoll( &player.mdl );
1247 else
1248 character_eval( &player.mdl );
1249
1250 character_draw( &player.mdl, (player.is_dead|player.in_air)? 0.0f: 1.0f );
1251 }
1252
1253 #endif /* PLAYER_H */