+ vg_info( "valid: %u\n", valid_count );
+ vg_info( "----------------------------\n" );
+}
+
+/*
+ * When going through a gate this is called for bookkeeping purposes
+ */
+VG_STATIC void world_routes_activate_entry_gate( world_instance *world,
+ ent_gate *rg )
+{
+ world_global.last_use = world_global.time;
+
+ /* disable all routes and leave the world */
+ if( rg->type == k_gate_type_nonlocel ){
+ for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
+ ent_route *route = mdl_arritm( &world->ent_route, i );
+ route->active_checkpoint = 0xffff;
+ }
+ return;
+ }
+
+ ent_gate *dest = mdl_arritm( &world->ent_gate, rg->target );
+
+ for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
+ ent_route *route = mdl_arritm( &world->ent_route, i );
+
+ u32 active_prev = route->active_checkpoint;
+ route->active_checkpoint = 0xffff;
+
+ for( u32 j=0; j<4; j++ ){
+ if( dest->routes[j] == i ){
+ for( u32 k=0; k<route->checkpoints_count; k++ ){
+ ent_checkpoint *cp = mdl_arritm( &world->ent_checkpoint,
+ route->checkpoints_start+k );
+
+ ent_gate *gk = mdl_arritm( &world->ent_gate, cp->gate_index );
+ gk = mdl_arritm( &world->ent_gate, gk->target );
+ if( gk == dest ){
+ route->active_checkpoint = k;
+ world_routes_time_lap( world, route );
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ dest->timing_version = world_global.current_run_version;
+ dest->timing_time = world_global.time;
+
+ world_global.current_run_version ++;
+}
+
+/* draw lines along the paths */
+VG_STATIC void world_routes_debug( world_instance *world )
+{
+ for( u32 i=0; i<mdl_arrcount(&world->ent_route_node); i++ ){
+ ent_route_node *rn = mdl_arritm(&world->ent_route_node,i);
+ vg_line_pt3( rn->co, 0.25f, VG__WHITE );
+ }
+
+ for( u32 i=0; i<mdl_arrcount(&world->ent_route); i++ ){
+ ent_route *route = mdl_arritm(&world->ent_route, i);
+
+ u32 colours[] = { 0xfff58142, 0xff42cbf5, 0xff42f56c, 0xfff542b3,
+ 0xff5442f5 };
+
+ u32 cc = 0xffcccccc;
+ if( route->active_checkpoint != 0xffff ){
+ cc = colours[i%vg_list_size(colours)];
+ }
+
+ for( int i=0; i<route->checkpoints_count; i++ ){
+ int i0 = route->checkpoints_start+i,
+ i1 = route->checkpoints_start+((i+1)%route->checkpoints_count);
+
+ ent_checkpoint *c0 = mdl_arritm(&world->ent_checkpoint, i0),
+ *c1 = mdl_arritm(&world->ent_checkpoint, i1);
+
+ ent_gate *start_gate = mdl_arritm( &world->ent_gate, c0->gate_index );
+ ent_gate *end_gate = mdl_arritm( &world->ent_gate, c1->gate_index );
+
+ v3f p0, p1;
+ v3_copy( start_gate->co[1], p0 );
+
+ for( int j=0; j<c0->path_count; j ++ ){
+ ent_path_index *index = mdl_arritm( &world->ent_path_index,
+ c0->path_start+j );
+
+ ent_route_node *rn = mdl_arritm( &world->ent_route_node,
+ index->index );
+
+ v3_copy( rn->co, p1 );
+ vg_line( p0, p1, cc );
+ v3_copy( p1, p0 );
+ }
+
+ v3_copy( end_gate->co[0], p1 );
+ vg_line( p0, p1, cc );
+ }
+ }
+}
+
+VG_STATIC
+void world_routes_place_curve( world_instance *world,
+ v4f h[3], v3f n0, v3f n2, scene_context *scene )
+{
+ float t;
+ v3f p, pd;
+ int last_valid=0;
+
+ float total_length = 0.0f,
+ travel_length = 0.0;
+
+ v3f last;
+ v3_copy( h[0], last );
+ for( int it=0; it<128; it ++ ){
+ t = (float)(it+1) * (1.0f/128.0f);
+ eval_bezier3( h[0], h[1], h[2], t, p );
+ total_length += v3_dist( p, last );
+ v3_copy( p, last );
+ }
+
+ float patch_size = 4.0f,
+ patch_count = ceilf( total_length / patch_size );
+
+ t = 0.0f;
+ v3_copy( h[0], last );
+
+ for( int it=0; it<128; it ++ ){
+ float const k_sample_dist = 0.0025f,
+ k_line_width = 1.5f;
+
+ eval_bezier3( h[0], h[1], h[2], t, p );
+ eval_bezier3( h[0], h[1], h[2], t+k_sample_dist, pd );
+
+ travel_length += v3_dist( p, last );
+
+ float mod = k_sample_dist / v3_dist( p, pd );
+
+ v3f v0,up, right;
+
+ v3_muls( n0, -(1.0f-t), up );
+ v3_muladds( up, n2, -t, up );
+ v3_normalize( up );
+
+ v3_sub( pd,p,v0 );
+ v3_cross( up, v0, right );
+ v3_normalize( right );
+
+ float cur_x = (1.0f-t)*h[0][3] + t*h[2][3];