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