+ /* p0,p1,p2 bezier patch is complete
+ * --------------------------------------*/
+ v3f surf0, surf2, n0, n2;
+
+ if( bh_closest_point( world->geo_bh, p[0], surf0, 5.0f ) == -1 )
+ v3_add( (v3f){0.0f,-0.1f,0.0f}, p[0], surf0 );
+
+ if( bh_closest_point( world->geo_bh, p[2], surf2, 5.0f ) == -1 )
+ v3_add( (v3f){0.0f,-0.1f,0.0f}, p[2], surf2 );
+
+ v3_sub( surf0, p[0], n0 );
+ v3_sub( surf2, p[2], n2 );
+ v3_normalize( n0 );
+ v3_normalize( n2 );
+
+ world_routes_place_curve( world, p, n0, n2, sc );
+
+ /* --- */
+ v4_copy( p[2], p[0] );
+ }
+ }
+
+ scene_copy_slice( sc, &route->sm );
+}
+
+/*
+ * Create the strips of colour that run through the world along course paths
+ */
+VG_STATIC void world_routes_generate( world_instance *world )
+{
+ vg_info( "Generating route meshes\n" );
+ vg_async_item *call = scene_alloc_async( &world->scene_lines,
+ &world->mesh_route_lines,
+ 200000, 300000 );
+
+ for( u32 i=0; i<mdl_arrcount(&world->ent_gate); i++ ){
+ ent_gate *gate = mdl_arritm( &world->ent_gate, i );
+ gate->ref_count = 0;
+ gate->route_count = 0;
+ }
+
+ for( u32 i=0; i<mdl_arrcount(&world->ent_route_node); i++ ){
+ ent_route_node *rn = mdl_arritm( &world->ent_route_node, i );
+ rn->ref_count = 0;
+ rn->ref_total = 0;
+ }
+
+ for( u32 k=0; k<mdl_arrcount(&world->ent_route); k++ ){
+ ent_route *route = mdl_arritm( &world->ent_route, k );
+
+ 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 );
+ start_gate = mdl_arritm( &world->ent_gate, start_gate->target );
+ start_gate->route_count ++;
+
+ if( !c0->path_count )