b271faa7bffd8b142c3303d11e828d231e11883e
[carveJwlIkooP6JGAAIwe30JlM.git] / world_routes.c
1 /*
2 * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 */
4
5 #ifndef ROUTES_C
6 #define ROUTES_C
7
8 #include <time.h>
9 #include "entity.h"
10 #include "world_routes.h"
11 #include "world_gate.h"
12 #include "world_load.h"
13 #include "highscores.h"
14 #include "network.h"
15
16 #include "font.h"
17 #include "pointcloud.h"
18 #include "gui.h"
19 #include "steam.h"
20
21 #include "shaders/scene_route.h"
22 #include "shaders/routeui.h"
23
24 #if 0
25 static
26 void world_routes_local_set_record( world_instance *world, ent_route *route,
27 f64 lap_time )
28 {
29 vg_success( " NEW LAP TIME: %f\n", lap_time );
30
31 if( route->anon.official_track_id != 0xffffffff ){
32 double time_centiseconds = lap_time * 100.0;
33 if( time_centiseconds > (float)0xfffe ) /* skill issue */
34 return;
35
36 struct track_info *ti = &track_infos[ route->anon.official_track_id ];
37 highscore_record *record = &ti->record;
38 record->trackid = route->anon.official_track_id;
39 record->datetime = time(NULL);
40 record->playerid = 0;
41 record->points = 0;
42 record->time = time_centiseconds;
43 ti->push = 1;
44
45 if( ti->achievement_id ){
46 steam_set_achievement( ti->achievement_id );
47 steam_store_achievements();
48 }
49 }
50 else{
51 vg_warn( "There is no associated track for this record...\n" );
52 }
53 }
54 #endif
55
56 static void world_routes_clear( world_instance *world )
57 {
58 for( u32 i=0; i<mdl_arrcount( &world->ent_route ); i++ ){
59 ent_route *route = mdl_arritm( &world->ent_route, i );
60 route->active_checkpoint = 0xffff;
61 }
62
63 for( u32 i=0; i<mdl_arrcount( &world->ent_gate ); i++ ){
64 ent_gate *rg = mdl_arritm( &world->ent_gate, i );
65 rg->timing_version = 0;
66 rg->timing_time = 0.0;
67 }
68
69 world_static.current_run_version += 4;
70 world_static.last_use = 0.0;
71 }
72
73 static void world_routes_time_lap( world_instance *world, ent_route *route )
74 {
75 vg_info( "------- time lap %s -------\n",
76 mdl_pstr(&world->meta,route->pstr_name) );
77
78 double start_time = 0.0;
79 u32 last_version=0;
80
81 u32 valid_count=0;
82
83 for( u32 i=0; i<route->checkpoints_count; i++ ){
84 u32 cpid = (i+route->active_checkpoint) % route->checkpoints_count;
85 cpid += route->checkpoints_start;
86
87 ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint, cpid );
88 ent_gate *rg = mdl_arritm( &world->ent_gate, cp->gate_index );
89 rg = mdl_arritm( &world->ent_gate, rg->target );
90
91 if( i == 1 ){
92 route->timing_base = rg->timing_time;
93 }
94
95 if( i == 0 )
96 start_time = rg->timing_time;
97 else{
98 if( last_version+1 == rg->timing_version ) valid_count ++;
99 else valid_count = 0;
100 }
101
102 last_version = rg->timing_version;
103 vg_info( "%u %f\n", rg->timing_version, rg->timing_time );
104 }
105
106 if( world_static.current_run_version == last_version+1 ){
107 valid_count ++;
108
109 if( route->checkpoints_count == 1 ){
110 route->timing_base = world_static.time;
111 }
112 }
113 else valid_count = 0;
114
115 vg_info( "%u %f\n", world_static.current_run_version, world_static.time );
116
117 if( valid_count==route->checkpoints_count ){
118 f64 lap_time = world_static.time - start_time;
119 //world_routes_local_set_record( world, route, lap_time );
120
121 if( route->anon.official_track_id != 0xffffffff ){
122 struct track_info *ti = &track_infos[ route->anon.official_track_id ];
123 if( ti->achievement_id ){
124 steam_set_achievement( ti->achievement_id );
125 steam_store_achievements();
126 }
127 }
128
129 addon_alias *alias = &world_static.addon_hub->alias;
130 if( world_static.active_instance )
131 alias = &world_static.addon_client->alias;
132
133 /* TODO: we should probably generate this once. and also ditch
134 * sub-worlds */
135 char mod_uid[ ADDON_UID_MAX ];
136 addon_alias_uid( &world_static.addon_client->alias, mod_uid );
137 network_publish_laptime( mod_uid,
138 mdl_pstr( &world->meta, route->pstr_name ),
139 lap_time );
140 }
141
142 route->valid_checkpoints = valid_count+1;
143
144 vg_info( "valid: %u\n", valid_count );
145 vg_info( "----------------------------\n" );
146 }
147
148 /*
149 * When going through a gate this is called for bookkeeping purposes
150 */
151 static void world_routes_activate_entry_gate( world_instance *world,
152 ent_gate *rg )
153 {
154 world_static.last_use = world_static.time;
155
156 /* disable all routes and leave the world */
157 if( rg->flags & k_ent_gate_nonlocal ){
158 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
159 ent_route *route = mdl_arritm( &world->ent_route, i );
160 route->active_checkpoint = 0xffff;
161 }
162 return;
163 }
164
165 ent_gate *dest = mdl_arritm( &world->ent_gate, rg->target );
166
167 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
168 ent_route *route = mdl_arritm( &world->ent_route, i );
169
170 u32 active_prev = route->active_checkpoint;
171 route->active_checkpoint = 0xffff;
172
173 for( u32 j=0; j<4; j++ ){
174 if( dest->routes[j] == i ){
175 for( u32 k=0; k<route->checkpoints_count; k++ ){
176 ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint,
177 route->checkpoints_start+k );
178
179 ent_gate *gk = mdl_arritm( &world->ent_gate, cp->gate_index );
180 gk = mdl_arritm( &world->ent_gate, gk->target );
181 if( gk == dest ){
182 route->active_checkpoint = k;
183 world_routes_time_lap( world, route );
184 break;
185 }
186 }
187 break;
188 }
189 }
190 }
191
192 dest->timing_version = world_static.current_run_version;
193 dest->timing_time = world_static.time;
194
195 world_static.current_run_version ++;
196 }
197
198 /* draw lines along the paths */
199 static void world_routes_debug( world_instance *world )
200 {
201 for( u32 i=0; i<mdl_arrcount(&world->ent_route_node); i++ ){
202 ent_route_node *rn = mdl_arritm(&world->ent_route_node,i);
203 vg_line_point( rn->co, 0.25f, VG__WHITE );
204 }
205
206 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
207 ent_route *route = mdl_arritm(&world->ent_route, i);
208
209 u32 colours[] = { 0xfff58142, 0xff42cbf5, 0xff42f56c, 0xfff542b3,
210 0xff5442f5 };
211
212 u32 cc = 0xffcccccc;
213 if( route->active_checkpoint != 0xffff ){
214 cc = colours[i%vg_list_size(colours)];
215 }
216
217 for( int i=0; i<route->checkpoints_count; i++ ){
218 int i0 = route->checkpoints_start+i,
219 i1 = route->checkpoints_start+((i+1)%route->checkpoints_count);
220
221 ent_checkpoint *c0 = mdl_arritm(&world->ent_checkpoint, i0),
222 *c1 = mdl_arritm(&world->ent_checkpoint, i1);
223
224 ent_gate *start_gate = mdl_arritm( &world->ent_gate, c0->gate_index );
225 ent_gate *end_gate = mdl_arritm( &world->ent_gate, c1->gate_index );
226
227 v3f p0, p1;
228 v3_copy( start_gate->co[1], p0 );
229
230 for( int j=0; j<c0->path_count; j ++ ){
231 ent_path_index *index = mdl_arritm( &world->ent_path_index,
232 c0->path_start+j );
233
234 ent_route_node *rn = mdl_arritm( &world->ent_route_node,
235 index->index );
236
237 v3_copy( rn->co, p1 );
238 vg_line( p0, p1, cc );
239 v3_copy( p1, p0 );
240 }
241
242 v3_copy( end_gate->co[0], p1 );
243 vg_line( p0, p1, cc );
244 }
245 }
246 }
247
248 static
249 void world_routes_pointcloud_spot( world_instance *world,
250 pointcloud_buffer *pcbuf,
251 v3f co, f32 radius, u32 samples, v4f colour )
252 {
253 v3f inv_ext;
254 v3_sub( pcbuf->boundary[1], pcbuf->boundary[0], inv_ext );
255 v3_div( (v3f){1.0f,1.0f,1.0f}, inv_ext, inv_ext );
256
257 for( u32 j=0; j<samples; j++ ){
258 if( pcbuf->count >= pcbuf->max )
259 return;
260
261 pointcloud_vert *vert = &pcbuf->buf[ pcbuf->count ++ ];
262
263 v3f sample, jitter, point;
264 vg_rand_sphere( jitter );
265 v3_muladds( co, jitter, radius, sample );
266
267 if( bh_closest_point( world->geo_bh, sample, point, radius*1.5f ) == -1 ){
268 v3_copy( sample, point );
269 }
270
271 v3f pos;
272 v3_sub( point, pcbuf->boundary[0], pos );
273 v3_mul( pos, inv_ext, pos );
274
275 float dist = 1.0f-(v3_length(jitter));
276
277 v4f final_colour;
278 v4_muls( colour, dist*dist, final_colour );
279
280 pointcloud_packvert( vert, pos, final_colour );
281 }
282 }
283
284 /*
285 * '
286 * .
287 * |
288 * |
289 * /#\
290 * -'###`-
291 */
292 static
293 void world_routes_pointcloud_tower( world_instance *world,
294 pointcloud_buffer *pcbuf,
295 v3f co, f32 radius, f32 height,
296 u32 samples, v4f colour )
297 {
298 v3f inv_ext;
299 v3_sub( pcbuf->boundary[1], pcbuf->boundary[0], inv_ext );
300 v3_div( (v3f){1.0f,1.0f,1.0f}, inv_ext, inv_ext );
301
302 for( u32 j=0; j<samples; j++ ){
303 if( pcbuf->count >= pcbuf->max )
304 return;
305
306 pointcloud_vert *vert = &pcbuf->buf[ pcbuf->count ++ ];
307
308 v3f point;
309 point[0] = vg_randf64()*2.0f-1.0f;
310 point[1] = 0.0f;
311 point[2] = vg_randf64()*2.0f-1.0f;
312 v3_normalize( point );
313 v3_muls( point, sqrtf(vg_randf64()), point );
314
315 f32 h = vg_randf64();
316 point[1] = h*h*h*height;
317 point[0] *= radius;
318 point[2] *= radius;
319
320 v3_add( point, co, point );
321 v3_sub( point, pcbuf->boundary[0], point );
322 v3_mul( point, inv_ext, point );
323
324 pointcloud_packvert( vert, point, colour );
325 }
326 }
327
328 static
329 void world_routes_place_curve( world_instance *world, ent_route *route,
330 v4f h[3], v3f n0, v3f n2, scene_context *scene,
331 pointcloud_buffer *pcbuf )
332 {
333 float t;
334 v3f p, pd;
335 int last_valid=0;
336
337 float total_length = 0.0f,
338 travel_length = 0.0;
339
340 v3f last;
341 v3_copy( h[0], last );
342 for( int it=0; it<128; it ++ ){
343 t = (float)(it+1) * (1.0f/128.0f);
344 eval_bezier3( h[0], h[1], h[2], t, p );
345 total_length += v3_dist( p, last );
346 v3_copy( p, last );
347 }
348
349 float patch_size = 4.0f,
350 patch_count = ceilf( total_length / patch_size );
351
352 t = 0.0f;
353 v3_copy( h[0], last );
354
355 for( int it=0; it<128; it ++ ){
356 float const k_sample_dist = 0.0025f,
357 k_line_width = 1.5f;
358
359 eval_bezier3( h[0], h[1], h[2], t, p );
360 eval_bezier3( h[0], h[1], h[2], t+k_sample_dist, pd );
361
362 travel_length += v3_dist( p, last );
363
364 float mod = k_sample_dist / v3_dist( p, pd );
365
366 v3f v0,up, right;
367
368 v3_muls( n0, -(1.0f-t), up );
369 v3_muladds( up, n2, -t, up );
370 v3_normalize( up );
371
372 v3_sub( pd,p,v0 );
373 v3_cross( up, v0, right );
374 v3_normalize( right );
375
376 float cur_x = (1.0f-t)*h[0][3] + t*h[2][3];
377
378 v3f sc, sa, sb, down;
379 v3_muladds( p, right, cur_x * k_line_width, sc );
380 v3_muladds( sc, up, 1.5f, sc );
381 v3_muladds( sc, right, k_line_width*0.95f, sa );
382 v3_muladds( sc, right, 0.0f, sb );
383 v3_muls( up, -1.0f, down );
384
385 ray_hit ha, hb;
386 ha.dist = 8.0f;
387 hb.dist = 8.0f;
388
389 int resa = ray_world( world, sa, down, &ha, k_material_flag_ghosts ),
390 resb = ray_world( world, sb, down, &hb, k_material_flag_ghosts );
391
392 if( pcbuf && resa ){
393 world_routes_pointcloud_spot( world, pcbuf, ha.pos,
394 12.0f, 10, route->colour );
395 }
396
397 if( resa && resb ){
398 struct world_surface *surfa = ray_hit_surface( world, &ha ),
399 *surfb = ray_hit_surface( world, &hb );
400
401 if( (surfa->info.flags & k_material_flag_skate_target) &&
402 (surfb->info.flags & k_material_flag_skate_target) )
403 {
404 scene_vert va, vb;
405
406 float gap = vg_fractf(cur_x*0.5f)*0.02f;
407
408 v3_muladds( ha.pos, up, 0.06f+gap, va.co );
409 v3_muladds( hb.pos, up, 0.06f+gap, vb.co );
410
411 scene_vert_pack_norm( &va, up );
412 scene_vert_pack_norm( &vb, up );
413
414 float t1 = (travel_length / total_length) * patch_count;
415 va.uv[0] = t1;
416 va.uv[1] = 0.0f;
417 vb.uv[0] = t1;
418 vb.uv[1] = 1.0f;
419
420 scene_push_vert( scene, &va );
421 scene_push_vert( scene, &vb );
422
423 if( last_valid ){
424 /* Connect them with triangles */
425 scene_push_tri( scene, (u32[3]){
426 last_valid+0-2, last_valid+1-2, last_valid+2-2} );
427 scene_push_tri( scene, (u32[3]){
428 last_valid+1-2, last_valid+3-2, last_valid+2-2} );
429 }
430
431 last_valid = scene->vertex_count;
432 }
433 else
434 last_valid = 0;
435 }
436 else
437 last_valid = 0;
438
439 if( t == 1.0f )
440 return;
441
442 t += 1.0f*mod;
443 if( t > 1.0f )
444 t = 1.0f;
445
446 v3_copy( p, last );
447 }
448 }
449
450 static void world_routes_gen_meshes( world_instance *world, u32 route_id,
451 scene_context *sc,
452 pointcloud_buffer *pcbuf )
453 {
454 ent_route *route = mdl_arritm( &world->ent_route, route_id );
455 u8 colour[4];
456 colour[0] = route->colour[0] * 255.0f;
457 colour[1] = route->colour[1] * 255.0f;
458 colour[2] = route->colour[2] * 255.0f;
459 colour[3] = route->colour[3] * 255.0f;
460
461 u32 last_valid = 0;
462
463 for( int i=0; i<route->checkpoints_count; i++ ){
464 int i0 = route->checkpoints_start+i,
465 i1 = route->checkpoints_start+((i+1)%route->checkpoints_count);
466
467 ent_checkpoint *c0 = mdl_arritm(&world->ent_checkpoint, i0),
468 *c1 = mdl_arritm(&world->ent_checkpoint, i1);
469
470 ent_gate *start_gate = mdl_arritm( &world->ent_gate, c0->gate_index );
471 start_gate = mdl_arritm( &world->ent_gate, start_gate->target );
472
473 ent_gate *end_gate = mdl_arritm( &world->ent_gate, c1->gate_index ),
474 *collector = mdl_arritm( &world->ent_gate, end_gate->target );
475
476 v4f p[3];
477
478 v3_add( (v3f){0.0f,0.1f,0.0f}, start_gate->co[0], p[0] );
479 p[0][3] = start_gate->ref_count;
480 p[0][3] -= (float)start_gate->route_count * 0.5f;
481 start_gate->ref_count ++;
482
483 if( !c0->path_count )
484 continue;
485
486 /* this is so that we get nice flow through the gates */
487 v3f temp_alignments[2];
488 ent_gate *both[] = { start_gate, end_gate };
489
490 for( int j=0; j<2; j++ ){
491 int pi = c0->path_start + ((j==1)? c0->path_count-1: 0);
492
493 ent_path_index *index = mdl_arritm( &world->ent_path_index, pi );
494 ent_route_node *rn = mdl_arritm( &world->ent_route_node,
495 index->index );
496 v3f v0;
497 v3_sub( rn->co, both[j]->co[0], v0 );
498 float d = v3_dot( v0, both[j]->to_world[2] );
499
500 v3_muladds( both[j]->co[0], both[j]->to_world[2], d,
501 temp_alignments[j] );
502 v3_add( (v3f){0.0f,0.1f,0.0f}, temp_alignments[j], temp_alignments[j]);
503 }
504
505
506 for( int j=0; j<c0->path_count; j ++ ){
507 ent_path_index *index = mdl_arritm( &world->ent_path_index,
508 c0->path_start+j );
509 ent_route_node *rn = mdl_arritm( &world->ent_route_node,
510 index->index );
511 if( j==0 || j==c0->path_count-1 )
512 if( j == 0 )
513 v3_copy( temp_alignments[0], p[1] );
514 else
515 v3_copy( temp_alignments[1], p[1] );
516 else
517 v3_copy( rn->co, p[1] );
518
519 p[1][3] = rn->ref_count;
520 p[1][3] -= (float)rn->ref_total * 0.5f;
521 rn->ref_count ++;
522
523 if( j+1 < c0->path_count ){
524 index = mdl_arritm( &world->ent_path_index,
525 c0->path_start+j+1 );
526 rn = mdl_arritm( &world->ent_route_node, index->index );
527
528 if( j+1 == c0->path_count-1 )
529 v3_lerp( p[1], temp_alignments[1], 0.5f, p[2] );
530 else
531 v3_lerp( p[1], rn->co, 0.5f, p[2] );
532
533 p[2][3] = rn->ref_count;
534 p[2][3] -= (float)rn->ref_total * 0.5f;
535 }
536 else{
537 v3_copy( end_gate->co[0], p[2] );
538 v3_add( (v3f){0.0f,0.1f,0.0f}, p[2], p[2] );
539 p[2][3] = collector->ref_count;
540
541 if( i == route->checkpoints_count-1)
542 p[2][3] -= 1.0f;
543
544 p[2][3] -= (float)collector->route_count * 0.5f;
545 //collector->ref_count ++;
546 }
547
548 /* p0,p1,p2 bezier patch is complete
549 * --------------------------------------*/
550 v3f surf0, surf2, n0, n2;
551
552 if( bh_closest_point( world->geo_bh, p[0], surf0, 5.0f ) == -1 )
553 v3_add( (v3f){0.0f,-0.1f,0.0f}, p[0], surf0 );
554
555 if( bh_closest_point( world->geo_bh, p[2], surf2, 5.0f ) == -1 )
556 v3_add( (v3f){0.0f,-0.1f,0.0f}, p[2], surf2 );
557
558 v3_sub( surf0, p[0], n0 );
559 v3_sub( surf2, p[2], n2 );
560 v3_normalize( n0 );
561 v3_normalize( n2 );
562
563 world_routes_place_curve( world, route, p, n0, n2, sc, pcbuf );
564
565 /* --- */
566 v4_copy( p[2], p[0] );
567 }
568 }
569
570 scene_copy_slice( sc, &route->sm );
571 }
572
573 static
574 struct world_surface *world_tri_index_surface( world_instance *world,
575 u32 index );
576
577 static f64 world_routes_scatter_surface_points( world_instance *world,
578 pointcloud_buffer *pcbuf,
579 f32 rate )
580 {
581 static f32 densities[] = {
582 [k_surface_prop_concrete] = 2.0f,
583 [k_surface_prop_grass] = 0.8f,
584 [k_surface_prop_metal] = 1.0f,
585 [k_surface_prop_wood] = 2.5f,
586 [k_surface_prop_tiles] = 4.0f
587 };
588
589 /* calculate total area */
590 f64 total_area = 0.0f;
591 for( u32 i=0; i<world->scene_geo.indice_count/3; i++ ){
592 u32 *tri = &world->scene_geo.arrindices[i*3];
593 struct world_surface *surf = world_tri_index_surface( world, tri[0] );
594
595 if( surf->info.shader == k_shader_boundary ||
596 surf->info.shader == k_shader_invisible ) continue;
597
598 if( !(surf->info.flags & k_material_flag_preview_visibile) ) continue;
599
600 scene_vert *va = &world->scene_geo.arrvertices[tri[0]],
601 *vb = &world->scene_geo.arrvertices[tri[1]],
602 *vc = &world->scene_geo.arrvertices[tri[2]];
603
604 v3f v0, v1, vn;
605 v3_sub( vb->co, va->co, v0 );
606 v3_sub( vc->co, va->co, v1 );
607 v3_cross( v0, v1, vn );
608 if( vn[1] < 0.0f ) continue;
609
610 f32 density = 1.0f;
611 if( surf->info.surface_prop < vg_list_size(densities) )
612 density = densities[surf->info.surface_prop];
613 total_area += v3_length(vn)*0.5f*density;
614 }
615
616 f32 accum = 0.0f;
617
618 u8 colour[] = { 80,80,80,255 };
619 v3f light_dir = {0.3f,0.8f,0.1f};
620 v3_normalize( light_dir );
621
622 v3f inv_ext;
623 v3_sub( pcbuf->boundary[1], pcbuf->boundary[0], inv_ext );
624 v3_div( (v3f){1.0f,1.0f,1.0f}, inv_ext, inv_ext );
625
626 for( u32 i=0; i<world->scene_geo.indice_count/3; i++ ){
627 u32 *tri = &world->scene_geo.arrindices[i*3];
628 struct world_surface *surf = world_tri_index_surface( world, tri[0] );
629
630 if( surf->info.shader == k_shader_boundary ||
631 surf->info.shader == k_shader_invisible ) continue;
632
633 if( !(surf->info.flags & k_material_flag_preview_visibile) ) continue;
634
635 scene_vert *va = &world->scene_geo.arrvertices[tri[0]],
636 *vb = &world->scene_geo.arrvertices[tri[1]],
637 *vc = &world->scene_geo.arrvertices[tri[2]];
638
639 v3f v0, v1, vn;
640 v3_sub( vb->co, va->co, v0 );
641 v3_sub( vc->co, va->co, v1 );
642 v3_cross( v0, v1, vn );
643 if( vn[1] < 0.0f ) continue;
644
645 f32 density = 1.0f;
646 if( surf->info.surface_prop < vg_list_size(densities) )
647 density = densities[surf->info.surface_prop];
648
649 f32 area = v3_length(vn)*0.5f*density;
650 accum += area;
651
652 v3_normalize( vn );
653
654 while( accum > rate ){
655 accum -= rate;
656
657 if( pcbuf->count >= pcbuf->max ) return total_area;
658
659 v2f co = { vg_randf64(), vg_randf64() };
660 if( v2_length2(co) > 0.5f ){
661 co[0] = 1.0f-co[0];
662 co[1] = 1.0f-co[1];
663 }
664
665 v3f pt;
666 v3_muls( v0, co[0], pt );
667 v3_muladds( pt, v1, co[1], pt );
668 v3_add( va->co, pt, pt );
669
670 if( pt[1] < world->water.height ) continue;
671 pointcloud_vert *vert = &pcbuf->buf[ pcbuf->count ++ ];
672
673 v3f pos;
674 v3_sub( pt, pcbuf->boundary[0], pos );
675 v3_mul( pos, inv_ext, pos );
676
677 static v4f colours[] = {
678 [k_surface_prop_concrete] = { 0.13, 0.15, 0.17, 1.0 },
679 [k_surface_prop_grass] = { 0.07, 0.1, 0.14, 1.0 },
680 [k_surface_prop_metal] = { 0.15, 0.19, 0.22, 1.0 },
681 [k_surface_prop_wood] = { 0.1, 0.13, 0.17, 1.0 },
682 [k_surface_prop_tiles] = { 0.05, 0.06, 0.07, 1.0 },
683 };
684
685 v4f col = {0.0f,0.0f,0.0f,0.0f};
686 if( surf->info.surface_prop < vg_list_size(colours) )
687 v4_copy( colours[surf->info.surface_prop], col );
688
689 f32 brightness = v3_dot(vn,light_dir)*0.5f+0.5f;
690 v3_muls( col, brightness, col );
691
692 pointcloud_packvert( vert, pos, col );
693 }
694 }
695
696 return total_area;
697 }
698
699 static void world_routes_surface_grid( world_instance *world,
700 pointcloud_buffer *pcbuf )
701 {
702 i32 const k_gridlines = 32,
703 k_gridres = 255;
704
705 v3f inv_ext;
706 v3_sub( pcbuf->boundary[1], pcbuf->boundary[0], inv_ext );
707 v3_div( (v3f){1.0f,1.0f,1.0f}, inv_ext, inv_ext );
708 v4f colour = {0.2f,0.2f,0.2f,1.0f};
709 v3f dir = {0.0f,-1.0f,0.0f};
710
711 for( u32 k=0; k<2; k++ ){
712 u32 a = k*2,
713 b = (k^0x1)*2;
714
715 for( i32 x=0; x<=k_gridlines; x++ ){
716 f32 t = (float)x / (float)k_gridlines,
717 px = vg_lerpf( pcbuf->boundary[0][a], pcbuf->boundary[1][a], t );
718
719 for( i32 z=0; z<=k_gridres; z++ ){
720 f32 tz = (float)z / (float)k_gridres,
721 pz = vg_lerpf(pcbuf->boundary[0][b],pcbuf->boundary[1][b], tz);
722
723 v3f ro, hit;
724 ro[a] = px;
725 ro[1] = 1000.0f;
726 ro[b] = pz;
727
728 bh_iter it;
729 bh_iter_init_ray( 0, &it, ro, dir, INFINITY );
730 i32 idx;
731
732 while( bh_next( world->geo_bh, &it, &idx ) ){
733 u32 *tri = &world->scene_geo.arrindices[ idx*3 ];
734 v3f vs[3];
735
736 u16 mask = k_material_flag_preview_visibile;
737 if( !(world->scene_geo.arrvertices[tri[0]].flags & mask) )
738 continue;
739
740 for( u32 i=0; i<3; i++ ){
741 v3_copy( world->scene_geo.arrvertices[tri[i]].co, vs[i] );
742 }
743
744 f32 t;
745 if( ray_tri( vs, ro, dir, &t ) ){
746 v3_muladds( ro, dir, t, hit );
747
748 if( world->water.enabled )
749 if( hit[1] < world->water.height )
750 continue;
751
752 if( pcbuf->count >= pcbuf->max ) return;
753
754 pointcloud_vert *vert = &pcbuf->buf[ pcbuf->count ++ ];
755
756 v3f co;
757 v3_sub( hit, pcbuf->boundary[0], co );
758 v3_mul( co, inv_ext, co );
759
760 pointcloud_packvert( vert, co, colour );
761 }
762 }
763 }
764 }
765 }
766 }
767
768 static void world_write_preview( addon_reg *reg, pointcloud_buffer *pcbuf ){
769 if( reg->alias.workshop_id ) return;
770
771 /*
772 * FIXME: BUG: cannot correctly handle workshop because there is a stalling
773 * call below, which deadlocks the scene upload. TODO: improve the async
774 * stack to handle out of order execution. MAYBE
775 */
776
777 char path_buf[4096];
778 vg_str path;
779 vg_strnull( &path, path_buf, 4096 );
780
781 addon_get_content_folder( reg, &path );
782 vg_strcat( &path, "/preview.bin" );
783
784 if( !vg_strgood( &path ) ) vg_fatal_error( "Path too long\n" );
785 FILE *fp = fopen( path_buf, "wb" );
786 if( !fp ) vg_fatal_error( "Cannot open '%s' for writing\n", path_buf );
787
788 fwrite( pcbuf, sizeof(struct pointcloud_buffer) +
789 sizeof(struct pointcloud_vert)*pcbuf->count, 1, fp );
790 fclose( fp );
791 vg_info( "written %s\n", path_buf );
792 }
793
794 /*
795 * Create the strips of colour that run through the world along course paths
796 */
797 static void world_gen_routes_generate( u32 instance_id ){
798 world_instance *world = &world_static.instances[ instance_id ];
799 vg_info( "Generating route meshes\n" );
800 vg_async_stall();
801
802 vg_rand_seed( 2000 );
803 vg_async_item *call_scene = scene_alloc_async( &world->scene_lines,
804 &world->mesh_route_lines,
805 200000, 300000 );
806
807 vg_async_item *call_pointcloud = NULL;
808 pointcloud_buffer *pcbuf = NULL;
809
810 if( instance_id <= 1 /*world_loader.generate_point_cloud*/ ){
811 call_pointcloud = vg_async_alloc(
812 sizeof(pointcloud_buffer) +
813 sizeof(pointcloud_vert)*POINTCLOUD_POINTS );
814 pcbuf = call_pointcloud->payload;
815 pcbuf->count = 0;
816 pcbuf->max = POINTCLOUD_POINTS;
817 pcbuf->op = k_pointcloud_op_clear;
818
819 v3f ext, mid, v0;
820 v3_sub( world->scene_geo.bbx[1], world->scene_geo.bbx[0], ext );
821 f32 maxe = v3_maxf( ext );
822 v3_fill( v0, maxe * 0.5f );
823 v3_muladds( world->scene_geo.bbx[0], ext, 0.5f, mid );
824 v3_add( mid, v0, pcbuf->boundary[1] );
825 v3_sub( mid, v0, pcbuf->boundary[0] );
826 }
827
828 for( u32 i=0; i<mdl_arrcount(&world->ent_gate); i++ ){
829 ent_gate *gate = mdl_arritm( &world->ent_gate, i );
830 gate->ref_count = 0;
831 gate->route_count = 0;
832 }
833
834 for( u32 i=0; i<mdl_arrcount(&world->ent_route_node); i++ ){
835 ent_route_node *rn = mdl_arritm( &world->ent_route_node, i );
836 rn->ref_count = 0;
837 rn->ref_total = 0;
838 }
839
840 for( u32 k=0; k<mdl_arrcount(&world->ent_route); k++ ){
841 ent_route *route = mdl_arritm( &world->ent_route, k );
842
843 for( int i=0; i<route->checkpoints_count; i++ ){
844 int i0 = route->checkpoints_start+i,
845 i1 = route->checkpoints_start+((i+1)%route->checkpoints_count);
846
847 ent_checkpoint *c0 = mdl_arritm(&world->ent_checkpoint, i0),
848 *c1 = mdl_arritm(&world->ent_checkpoint, i1);
849
850 ent_gate *start_gate = mdl_arritm( &world->ent_gate, c0->gate_index );
851 start_gate = mdl_arritm( &world->ent_gate, start_gate->target );
852 start_gate->route_count ++;
853
854 if( !c0->path_count )
855 continue;
856
857 for( int j=0; j<c0->path_count; j ++ ){
858 ent_path_index *index = mdl_arritm( &world->ent_path_index,
859 c0->path_start+j );
860 ent_route_node *rn = mdl_arritm( &world->ent_route_node,
861 index->index );
862 rn->ref_total ++;
863 }
864 }
865 }
866
867 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
868 world_routes_gen_meshes( world, i, &world->scene_lines, pcbuf );
869 }
870
871 if( instance_id <= 1 /*world_loader.generate_point_cloud*/ ){
872 f64 area = 0.0;
873
874 #if VG_RELEASE
875 area = world_routes_scatter_surface_points( world, pcbuf, 16.0f );
876 world_routes_surface_grid( world, pcbuf );
877
878 for( u32 i=0; i<mdl_arrcount( &world->ent_gate ); i++ ){
879 ent_gate *gate = mdl_arritm( &world->ent_gate, i );
880
881 world_routes_pointcloud_tower( world, pcbuf, gate->co[0],
882 2.0f, 50.0f, 128,
883 (v4f){0.2f,0.2f,0.2f,1.0f} );
884 }
885 #endif
886
887 vg_info( "Distributed %u points over %fkm^2!\n",
888 pcbuf->count, area/1e6f );
889
890 world_write_preview( instance_id? world_static.addon_client:
891 world_static.addon_hub,
892 pcbuf );
893 vg_async_dispatch( call_pointcloud, async_pointcloud_sub );
894 }
895
896 vg_async_dispatch( call_scene, async_scene_upload );
897 world_routes_clear( world );
898 }
899
900 /* load all routes from model header */
901 static void world_gen_routes_ent_init( world_instance *world ){
902 vg_info( "Initializing routes\n" );
903
904 for( u32 i=0; i<mdl_arrcount(&world->ent_gate); i++ ){
905 ent_gate *gate = mdl_arritm( &world->ent_gate, i );
906 for( u32 j=0; j<4; j++ ){
907 gate->routes[j] = 0xffff;
908 }
909 }
910
911 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
912 ent_route *route = mdl_arritm(&world->ent_route,i);
913 mdl_transform_m4x3( &route->anon.transform, route->board_transform );
914
915 route->anon.official_track_id = 0xffffffff;
916 for( u32 j=0; j<vg_list_size(track_infos); j ++ ){
917 if( !strcmp(track_infos[j].name,
918 mdl_pstr(&world->meta,route->pstr_name))){
919 route->anon.official_track_id = j;
920 }
921 }
922
923 for( u32 j=0; j<route->checkpoints_count; j++ ){
924 u32 id = route->checkpoints_start + j;
925 ent_checkpoint *cp = mdl_arritm(&world->ent_checkpoint,id);
926
927 ent_gate *gate = mdl_arritm( &world->ent_gate, cp->gate_index );
928
929 for( u32 k=0; k<4; k++ ){
930 if( gate->routes[k] == 0xffff ){
931 gate->routes[k] = i;
932 break;
933 }
934 }
935
936 if( (gate->flags & k_ent_gate_linked) &
937 !(gate->flags & k_ent_gate_nonlocal) ){
938 gate = mdl_arritm(&world->ent_gate, gate->target );
939
940 for( u32 k=0; k<4; k++ ){
941 if( gate->routes[k] == i ){
942 vg_error( "already assigned route to gate\n" );
943 break;
944 }
945 if( gate->routes[k] == 0xffff ){
946 gate->routes[k] = i;
947 break;
948 }
949 }
950 }
951 }
952 }
953
954 for( u32 i=0; i<mdl_arrcount(&world->ent_gate); i++ ){
955 ent_gate *gate = mdl_arritm( &world->ent_gate, i );
956 }
957
958 world_routes_clear( world );
959 }
960
961 /*
962 * -----------------------------------------------------------------------------
963 * Events
964 * -----------------------------------------------------------------------------
965 */
966
967 static void world_routes_init(void)
968 {
969 world_static.current_run_version = 200;
970 world_static.time = 300.0;
971 world_static.last_use = 0.0;
972
973 shader_scene_route_register();
974 shader_routeui_register();
975 }
976
977 static void world_routes_update( world_instance *world )
978 {
979 world_static.time += vg.time_delta;
980
981 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
982 ent_route *route = mdl_arritm( &world->ent_route, i );
983
984 int target = (route->active_checkpoint == 0xffff? 0: 1) ||
985 skaterift.activity == k_skaterift_respawning;
986 route->factive = vg_lerpf( route->factive, target,
987 0.6f*vg.time_frame_delta );
988 }
989
990 for( u32 i=0; i<world_render.text_particle_count; i++ ){
991 struct text_particle *particle = &world_render.text_particles[i];
992 rb_object_debug( &particle->obj, VG__RED );
993 }
994 }
995
996 static void world_routes_fixedupdate( world_instance *world ){
997 rb_solver_reset();
998
999 for( u32 i=0; i<world_render.text_particle_count; i++ ){
1000 struct text_particle *particle = &world_render.text_particles[i];
1001
1002 if( rb_global_has_space() ){
1003 rb_ct *buf = rb_global_buffer();
1004
1005 int l = rb_sphere__scene( particle->obj.rb.to_world,
1006 &particle->obj.inf.sphere,
1007 NULL, &world->rb_geo.inf.scene, buf,
1008 k_material_flag_ghosts );
1009
1010 for( int j=0; j<l; j++ ){
1011 buf[j].rba = &particle->obj.rb;
1012 buf[j].rbb = &world->rb_geo.rb;
1013 }
1014
1015 rb_contact_count += l;
1016 }
1017 }
1018
1019 rb_presolve_contacts( rb_contact_buffer, rb_contact_count );
1020
1021 for( int i=0; i<rb_contact_count; i++ ){
1022 rb_contact_restitution( rb_contact_buffer+i, vg_randf64() );
1023 }
1024
1025 for( int i=0; i<6; i++ ){
1026 rb_solve_contacts( rb_contact_buffer, rb_contact_count );
1027 }
1028
1029 for( u32 i=0; i<world_render.text_particle_count; i++ ){
1030 struct text_particle *particle = &world_render.text_particles[i];
1031 rb_iter( &particle->obj.rb );
1032 }
1033
1034 for( u32 i=0; i<world_render.text_particle_count; i++ ){
1035 struct text_particle *particle = &world_render.text_particles[i];
1036 rb_update_transform( &particle->obj.rb );
1037 }
1038 }
1039
1040 static void bind_terrain_noise(void);
1041 static void world_bind_light_array( world_instance *world,
1042 GLuint shader, GLuint location,
1043 int slot );
1044 static void world_bind_light_index( world_instance *world,
1045 GLuint shader, GLuint location,
1046 int slot );
1047
1048 static void world_routes_update_timer_texts( world_instance *world ){
1049 world_render.timer_text_count = 0;
1050
1051 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
1052 ent_route *route = mdl_arritm( &world->ent_route, i );
1053
1054 if( route->active_checkpoint != 0xffff ){
1055 u32 next = route->active_checkpoint+1;
1056 next = next % route->checkpoints_count;
1057 next += route->checkpoints_start;
1058
1059 ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint, next );
1060 ent_gate *gate = mdl_arritm( &world->ent_gate, cp->gate_index );
1061 ent_gate *dest = mdl_arritm( &world->ent_gate, gate->target );
1062
1063 u32 j=0;
1064 for( ; j<4; j++ ){
1065 if( dest->routes[j] == i ){
1066 break;
1067 }
1068 }
1069
1070 float h0 = 0.8f,
1071 h1 = 1.2f,
1072 depth = 0.4f,
1073 size = 0.4f;
1074
1075 struct timer_text *text =
1076 &world_render.timer_texts[ world_render.timer_text_count ++ ];
1077
1078 text->gate = gate;
1079 text->route = route;
1080
1081 if( route->valid_checkpoints >= route->checkpoints_count ){
1082 double lap_time = world_static.time - route->timing_base,
1083 time_centiseconds = lap_time * 100.0;
1084
1085 if( time_centiseconds > (float)0xfffe ) time_centiseconds = 0.0;
1086
1087 u16 centiseconds = time_centiseconds,
1088 seconds = centiseconds / 100,
1089 minutes = seconds / 60;
1090
1091 centiseconds %= 100;
1092 seconds %= 60;
1093 minutes %= 60;
1094
1095 if( minutes > 9 ) minutes = 9;
1096
1097 int j=0;
1098 if( minutes ){
1099 highscore_intr( text->text, minutes, 1, ' ' ); j++;
1100 text->text[j++] = ':';
1101 }
1102
1103 if( seconds >= 10 || minutes ){
1104 highscore_intr( text->text+j, seconds, 2, '0' ); j+=2;
1105 }
1106 else{
1107 highscore_intr( text->text+j, seconds, 1, '0' ); j++;
1108 }
1109
1110 text->text[j++] = '.';
1111 highscore_intr( text->text+j, centiseconds, 1, '0' ); j++;
1112 text->text[j] = '\0';
1113 }
1114 else{
1115 highscore_intr( text->text, route->valid_checkpoints, 1, ' ' );
1116 text->text[1] = '/';
1117 highscore_intr( text->text+2, route->checkpoints_count+1, 1, ' ' );
1118 text->text[3] = '\0';
1119 }
1120
1121 gui_font3d.font = &gui.font;
1122 float align_r = font3d_string_width( 0, text->text );
1123 align_r *= size;
1124
1125 v3f positions[] = {
1126 { -0.92f, h0, depth },
1127 { 0.92f - align_r, h0, depth },
1128 { -0.92f, h1, depth },
1129 { 0.92f - align_r, h1, depth },
1130 };
1131
1132 if( dest->route_count == 1 ){
1133 positions[0][0] = -align_r*0.5f;
1134 positions[0][1] = h1;
1135 }
1136
1137 m4x3f mmdl;
1138 ent_gate_get_mdl_mtx( gate, mmdl );
1139
1140 m3x3_copy( mmdl, text->transform );
1141 float ratio = v3_length(text->transform[0]) /
1142 v3_length(text->transform[1]);
1143
1144 m3x3_scale( text->transform, (v3f){ size, size*ratio, 0.1f } );
1145 m4x3_mulv( mmdl, positions[j], text->transform[3] );
1146 }
1147 }
1148 }
1149
1150 static void world_routes_fracture( world_instance *world, ent_gate *gate,
1151 v3f imp_co, v3f imp_v )
1152 {
1153 world_render.text_particle_count = 0;
1154
1155 for( u32 i=0; i<world_render.timer_text_count; i++ ){
1156 struct timer_text *text = &world_render.timer_texts[i];
1157
1158 if( text->gate != gate ) continue;
1159
1160 m4x3f transform;
1161 m4x3_mul( gate->transport, text->transform, transform );
1162
1163 v3f co, s;
1164 v4f q;
1165 m4x3_decompose( transform, co, q, s );
1166
1167 v3f offset;
1168 v3_zero( offset );
1169
1170 v4f colour;
1171 float brightness = 0.3f + world->ub_lighting.g_day_phase;
1172 v3_muls( text->route->colour, brightness, colour );
1173 colour[3] = 1.0f-text->route->factive;
1174
1175 for( u32 j=0;; j++ ){
1176 char c = text->text[j];
1177 if( !c ) break;
1178
1179 ent_glyph *glyph = font3d_glyph( &gui.font, 0, c );
1180 if( !glyph ) continue;
1181
1182 if( c >= (u32)'0' && c <= (u32)'9' && glyph->indice_count ){
1183 struct text_particle *particle =
1184 &world_render.text_particles[world_render.text_particle_count++];
1185
1186 particle->glyph = glyph;
1187 v4_copy( colour, particle->colour );
1188
1189 v3f origin;
1190 v2_muls( glyph->size, 0.5f, origin );
1191 origin[2] = -0.5f;
1192
1193 v3f world_co;
1194
1195 v3_add( offset, origin, world_co );
1196 m4x3_mulv( transform, world_co, world_co );
1197
1198 float r = vg_maxf( s[0]*glyph->size[0], s[1]*glyph->size[1] )*0.5f;
1199
1200 m3x3_identity( particle->mlocal );
1201 m3x3_scale( particle->mlocal, s );
1202 origin[2] *= s[2];
1203 v3_muls( origin, -1.0f, particle->mlocal[3] );
1204
1205 v3_copy( world_co, particle->obj.rb.co );
1206 v3_muls( imp_v, 1.0f+vg_randf64(), particle->obj.rb.v );
1207 particle->obj.rb.v[1] += 2.0f;
1208
1209 v4_copy( q, particle->obj.rb.q );
1210 particle->obj.rb.w[0] = vg_randf64()*2.0f-1.0f;
1211 particle->obj.rb.w[1] = vg_randf64()*2.0f-1.0f;
1212 particle->obj.rb.w[2] = vg_randf64()*2.0f-1.0f;
1213
1214 particle->obj.type = k_rb_shape_sphere;
1215 particle->obj.inf.sphere.radius = r*0.6f;
1216
1217 rb_init_object( &particle->obj );
1218 }
1219 offset[0] += glyph->size[0];
1220 }
1221 }
1222 }
1223
1224 static void render_gate_markers( int run_id, ent_gate *gate ){
1225 for( u32 j=0; j<4; j++ ){
1226 if( gate->routes[j] == run_id ){
1227 m4x3f mmdl;
1228 ent_gate_get_mdl_mtx( gate, mmdl );
1229 shader_model_gate_uMdl( mmdl );
1230 mdl_draw_submesh( &world_gates.sm_marker[j] );
1231 break;
1232 }
1233 }
1234 }
1235
1236 static void render_world_routes( world_instance *world, camera *cam,
1237 int layer_depth ){
1238 m4x3f identity_matrix;
1239 m4x3_identity( identity_matrix );
1240
1241 shader_scene_route_use();
1242 shader_scene_route_uTexGarbage(0);
1243 world_link_lighting_ub( world, _shader_scene_route.id );
1244 world_bind_position_texture( world, _shader_scene_route.id,
1245 _uniform_scene_route_g_world_depth, 2 );
1246 world_bind_light_array( world, _shader_scene_route.id,
1247 _uniform_scene_route_uLightsArray, 3 );
1248 world_bind_light_index( world, _shader_scene_route.id,
1249 _uniform_scene_route_uLightsIndex, 4 );
1250 bind_terrain_noise();
1251
1252 shader_scene_route_uPv( cam->mtx.pv );
1253 shader_scene_route_uPvmPrev( cam->mtx_prev.pv );
1254 shader_scene_route_uMdl( identity_matrix );
1255 shader_scene_route_uCamera( cam->transform[3] );
1256
1257 mesh_bind( &world->mesh_route_lines );
1258
1259 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
1260 ent_route *route = mdl_arritm( &world->ent_route, i );
1261
1262 v4f colour;
1263 v3_lerp( (v3f){0.7f,0.7f,0.7f}, route->colour, route->factive, colour );
1264 colour[3] = route->factive*0.2f;
1265
1266 shader_scene_route_uColour( colour );
1267 mdl_draw_submesh( &route->sm );
1268 }
1269
1270 /* timers
1271 * ---------------------------------------------------- */
1272 if( layer_depth == 0 ){
1273 font3d_bind( &gui.font, k_font_shader_default, 0, world, cam );
1274
1275 for( u32 i=0; i<world_render.timer_text_count; i++ ){
1276 struct timer_text *text = &world_render.timer_texts[i];
1277
1278 v4f colour;
1279 float brightness = 0.3f + world->ub_lighting.g_day_phase;
1280 v3_muls( text->route->colour, brightness, colour );
1281 colour[3] = 1.0f-text->route->factive;
1282
1283 shader_model_font_uColour( colour );
1284 font3d_simple_draw( 0, text->text, cam, text->transform );
1285 }
1286
1287 shader_model_font_uOffset( (v4f){0.0f,0.0f,0.0f,1.0f} );
1288
1289 for( u32 i=0; i<world_render.text_particle_count; i++ ){
1290 struct text_particle *particle = &world_render.text_particles[i];
1291
1292 m4x4f prev_mtx;
1293
1294 m4x3_expand( particle->mdl, prev_mtx );
1295 m4x4_mul( cam->mtx_prev.pv, prev_mtx, prev_mtx );
1296
1297 shader_model_font_uPvmPrev( prev_mtx );
1298
1299 v4f q;
1300 m4x3f model;
1301 rb_extrapolate( &particle->obj.rb, model[3], q );
1302 q_m3x3( q, model );
1303
1304 m4x3_mul( model, particle->mlocal, particle->mdl );
1305 shader_model_font_uMdl( particle->mdl );
1306 shader_model_font_uColour( particle->colour );
1307
1308 mesh_drawn( particle->glyph->indice_start,
1309 particle->glyph->indice_count );
1310 }
1311 }
1312
1313 /* gate markers
1314 * ---------------------------------------------------- */
1315
1316 shader_model_gate_use();
1317 shader_model_gate_uPv( cam->mtx.pv );
1318 shader_model_gate_uCam( cam->pos );
1319 shader_model_gate_uTime( vg.time*0.25f );
1320 shader_model_gate_uInvRes( (v2f){
1321 1.0f / (float)vg.window_x,
1322 1.0f / (float)vg.window_y });
1323
1324 mesh_bind( &world_gates.mesh );
1325
1326 /* skip writing into the motion vectors for this */
1327 glDrawBuffers( 1, (GLenum[]){ GL_COLOR_ATTACHMENT0 } );
1328 glDisable( GL_CULL_FACE );
1329
1330 if( skaterift.activity == k_skaterift_respawning ){
1331 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
1332 ent_route *route = mdl_arritm( &world->ent_route, i );
1333
1334 v4f colour;
1335 v3_muls( route->colour, 1.6666f, colour );
1336 colour[3] = 0.0f;
1337
1338 shader_model_gate_uColour( colour );
1339
1340 for( u32 j=0; j<mdl_arrcount(&world->ent_gate); j ++ ){
1341 ent_gate *gate = mdl_arritm( &world->ent_gate, j );
1342 if( !(gate->flags & k_ent_gate_nonlocal) )
1343 render_gate_markers( i, gate );
1344 }
1345 }
1346 }
1347 else{
1348 for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
1349 ent_route *route = mdl_arritm( &world->ent_route, i );
1350
1351 if( route->active_checkpoint != 0xffff ){
1352 v4f colour;
1353 float brightness = 0.3f + world->ub_lighting.g_day_phase;
1354 v3_muls( route->colour, brightness, colour );
1355 colour[3] = 1.0f-route->factive;
1356
1357 shader_model_gate_uColour( colour );
1358
1359 u32 next = route->active_checkpoint+1+layer_depth;
1360 next = next % route->checkpoints_count;
1361 next += route->checkpoints_start;
1362
1363 ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint, next );
1364 ent_gate *gate = mdl_arritm( &world->ent_gate, cp->gate_index );
1365 render_gate_markers( i, gate );
1366 }
1367 }
1368 }
1369 glEnable( GL_CULL_FACE );
1370 glDrawBuffers( 2, (GLenum[]){ GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 } );
1371 }
1372
1373 #endif /* ROUTES_C */