+VG_STATIC void world_link_nonlocal_gates( int index_a, int index_b )
+{
+ vg_info( "Linking non-local gates\n" );
+ world_instance *a = &world_global.worlds[ index_a ],
+ *b = &world_global.worlds[ index_b ];
+
+ for( int i=0; i<a->nonlocalgate_count; i++ )
+ {
+ struct nonlocal_gate *ga = &a->nonlocal_gates[i];
+ struct classtype_gate *ga_inf = mdl_get_entdata( a->meta, ga->node );
+ const char *ga_name = mdl_pstr( a->meta, ga_inf->target );
+
+ for( int j=0; j<b->nonlocalgate_count; j++ )
+ {
+ struct nonlocal_gate *gb = &b->nonlocal_gates[j];
+ struct classtype_gate *gb_inf = mdl_get_entdata( b->meta, gb->node );
+ const char *gb_name = mdl_pstr( b->meta, gb_inf->target );
+
+ if( !strcmp( ga_name, gb_name ) )
+ {
+ vg_success( "Created longjump for ID '%s'\n", ga_name );
+
+ v4f qYflip;
+ q_axis_angle( qYflip, (v3f){0.0f,1.0f,0.0f}, VG_PIf );
+
+ /* TODO: Gates are created very wonkily. refactor. */
+ ga->target_map_index = index_b;
+ gb->target_map_index = index_a;
+ ga->working = 1;
+ gb->working = 1;
+
+ v4_copy( ga->node->q, ga->gate.q[0] );
+ v4_copy( gb->node->q, ga->gate.q[1] );
+ v3_copy( ga->node->co, ga->gate.co[0] );
+ v3_copy( gb->node->co, ga->gate.co[1] );
+
+ v4_copy( gb->node->q, gb->gate.q[0] );
+ v4_copy( ga->node->q, gb->gate.q[1] );
+ v3_copy( gb->node->co, gb->gate.co[0] );
+ v3_copy( ga->node->co, gb->gate.co[1] );
+
+ /* reverse B's direction */
+ q_mul( gb->gate.q[0], qYflip, gb->gate.q[0] );
+ q_mul( gb->gate.q[1], qYflip, gb->gate.q[1] );
+ q_normalize( gb->gate.q[0] );
+ q_normalize( gb->gate.q[1] );
+
+ gate_transform_update( &ga->gate );
+ gate_transform_update( &gb->gate );
+ }
+ }
+ }
+}
+
+VG_STATIC float colour_luminance( v3f v )
+{
+ return v3_dot( v, (v3f){0.2126f, 0.7152f, 0.0722f} );
+}
+
+VG_STATIC float calc_light_influence( world_instance *world, v3f position,
+ v3f normal, int light )
+{
+ struct world_light *world_light = &world->lights[ light ];
+ struct classtype_world_light *inf = world_light->inf;
+
+ v3f light_delta;
+ v3_sub( world_light->node->co, position, light_delta );
+ v3_muls( light_delta, 10.0f, light_delta );
+
+ float quadratic = v3_dot( light_delta, light_delta ),
+ attenuation = 1.0f/( 1.0f + quadratic );
+
+ v3_normalize( light_delta );
+ //attenuation *= vg_maxf( 0.0, v3_dot( light_delta, normal ) );
+
+ float quadratic_light = attenuation * colour_luminance( inf->colour );
+
+ if( inf->type == k_light_type_point )
+ {
+ return quadratic_light;
+ }
+ else if( inf->type == k_light_type_spot )
+ {
+ v3f dir;
+ q_mulv( world_light->node->q, (v3f){0.0f,1.0f,0.0f}, dir );
+
+ float spot_theta = vg_maxf( 0.0f, v3_dot( light_delta, dir ) ),
+ falloff = spot_theta >= 0.0f? 1.0f: 0.0f;
+
+ return quadratic_light * falloff;
+ }
+ else
+ return 0.0f;
+}
+
+VG_STATIC void world_scene_compute_light_clusters( world_instance *world,
+ scene *sc )
+{
+ for( int i=0; i<sc->vertex_count; i++ )
+ {
+ scene_vert *vert = &sc->arrvertices[i];
+ vert->lights[0] = 0;
+ vert->lights[1] = 1;
+ vert->lights[2] = 2;
+ vert->lights[3] = 3;
+
+ float influences[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+
+ v3f co, norm;
+ v3_copy( vert->co, co );
+
+ norm[0] = vert->norm[0];
+ norm[1] = vert->norm[1];
+ norm[2] = vert->norm[2];
+
+ v3_muls( norm, 1.0f/127.0f, norm );
+
+ for( int j=0; j<world->light_count; j ++ )
+ {
+ float influence = calc_light_influence( world, co, norm, j );
+
+ int best_pos = 4;
+ for( int k=best_pos-1; k>=0; k -- )
+ if( influence > influences[k] )
+ best_pos = k;
+
+ if( best_pos < 4 )
+ {
+ for( int k=3; k>best_pos; k -- )
+ {
+ influences[k] = influences[k-1];
+ vert->lights[k] = vert->lights[k-1];
+ }
+
+ influences[best_pos] = influence;
+ vert->lights[best_pos] = j;
+ }
+ }
+ }
+}
+
+VG_STATIC void world_generate( world_instance *world )