void map::generate_lightmap(game* g) { memset(lm, 0, sizeof(lm)); memset(sm, 0, sizeof(sm)); const int dir_x[] = { 1, 0 , -1, 0 }; const int dir_y[] = { 0, 1 , 0, -1 }; const int dir_d[] = { 180, 270, 0, 90 }; const float luminance = g->u.active_light(); const float natural_light = g->natural_light_level(); // Daylight vision handling returned back to map due to issues it causes here if (natural_light > LIGHT_SOURCE_BRIGHT) { // Apply sunlight, first light source so just assign for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { // In bright light indoor light exists to some degree if (!g->m.is_outside(sx, sy)) lm[sx][sy] = LIGHT_AMBIENT_LOW; } } } // Apply player light sources if (luminance > LIGHT_AMBIENT_LOW) apply_light_source(g->u.posx, g->u.posy, luminance); for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { const ter_id terrain = g->m.ter(sx, sy); const std::vector<item> items = g->m.i_at(sx, sy); const field current_field = g->m.field_at(sx, sy); // When underground natural_light is 0, if this changes we need to revisit if (natural_light > LIGHT_AMBIENT_LOW) { if (!g->m.is_outside(sx, sy)) { // Apply light sources for external/internal divide for(int i = 0; i < 4; ++i) { if (INBOUNDS(sx + dir_x[i], sy + dir_y[i]) && g->m.is_outside(sx + dir_x[i], sy + dir_y[i])) { if (INBOUNDS(sx, sy) && g->m.is_outside(0, 0)) lm[sx][sy] = natural_light; if (g->m.light_transparency(sx, sy) > LIGHT_TRANSPARENCY_SOLID) apply_light_arc(sx, sy, dir_d[i], natural_light); } } } } if (items.size() == 1 && items[0].type->id == itm_flashlight_on) apply_light_source(sx, sy, 20); if(terrain == t_lava) apply_light_source(sx, sy, 50); if(terrain == t_console) apply_light_source(sx, sy, 3); if (items.size() == 1 && items[0].type->id == itm_candle_lit) apply_light_source(sx, sy, 4); if(terrain == t_emergency_light) apply_light_source(sx, sy, 3); // TODO: [lightmap] Attach light brightness to fields switch(current_field.type) { case fd_fire: if (3 == current_field.density) apply_light_source(sx, sy, 160); else if (2 == current_field.density) apply_light_source(sx, sy, 60); else apply_light_source(sx, sy, 16); break; case fd_fire_vent: case fd_flame_burst: apply_light_source(sx, sy, 8); break; case fd_electricity: if (3 == current_field.density) apply_light_source(sx, sy, 8); else if (2 == current_field.density) apply_light_source(sx, sy, 1); else apply_light_source(sx, sy, LIGHT_SOURCE_LOCAL); // kinda a hack as the square will still get marked break; } } } for (int i = 0; i < g->z.size(); ++i) { int mx = g->z[i].posx; int my = g->z[i].posy; if (INBOUNDS(mx, my)) { if (g->z[i].has_effect(ME_ONFIRE)) { apply_light_source(mx, my, 3); } // TODO: [lightmap] Attach natural light brightness to creatures // TODO: [lightmap] Allow creatures to have light attacks (ie: eyebot) // TODO: [lightmap] Allow creatures to have facing and arc lights switch (g->z[i].type->id) { case mon_zombie_electric: apply_light_source(mx, my, 1); break; case mon_turret: apply_light_source(mx, my, 2); break; case mon_flaming_eye: apply_light_source(mx, my, LIGHT_SOURCE_BRIGHT); break; case mon_manhack: apply_light_source(mx, my, LIGHT_SOURCE_LOCAL); break; } } } // Apply any vehicle light sources VehicleList vehs = g->m.get_vehicles(); for(int v = 0; v < vehs.size(); ++v) { if(vehs[v].v->lights_on) { int dir = vehs[v].v->face.dir(); for (std::vector<int>::iterator part = vehs[v].v->external_parts.begin(); part != vehs[v].v->external_parts.end(); ++part) { int px = vehs[v].x + vehs[v].v->parts[*part].precalc_dx[0]; int py = vehs[v].y + vehs[v].v->parts[*part].precalc_dy[0]; if(INBOUNDS(px, py)) { int dpart = vehs[v].v->part_with_feature(*part , vpf_light); if (dpart >= 0) { float luminance = vehs[v].v->part_info(dpart).power; if (luminance > LL_LIT) { apply_light_arc(px, py, dir, luminance); } } } } } } }
void map::generate_lightmap() { memset(lm, 0, sizeof(lm)); memset(sm, 0, sizeof(sm)); /* Bulk light sources wastefully cast rays into neighbors; a burning hospital can produce significant slowdown, so for stuff like fire and lava: * Step 1: Store the position and luminance in buffer via add_light_source, for efficient checking of neighbors. * Step 2: After everything else, iterate buffer and apply_light_source only in non-redundant directions * Step 3: Profit! */ memset(light_source_buffer, 0, sizeof(light_source_buffer)); const int dir_x[] = { 1, 0 , -1, 0 }; const int dir_y[] = { 0, 1 , 0, -1 }; const int dir_d[] = { 180, 270, 0, 90 }; const float held_luminance = g->u.active_light(); const float natural_light = g->natural_light_level(); if (natural_light > LIGHT_SOURCE_BRIGHT) { // Apply sunlight, first light source so just assign for(int sx = DAYLIGHT_LEVEL - (natural_light / 2); sx < LIGHTMAP_CACHE_X - (natural_light / 2); ++sx) { for(int sy = DAYLIGHT_LEVEL - (natural_light / 2); sy < LIGHTMAP_CACHE_Y - (natural_light / 2); ++sy) { // In bright light indoor light exists to some degree if (!is_outside(sx, sy)) { lm[sx][sy] = LIGHT_AMBIENT_LOW; } else if (g->u.posx == sx && g->u.posy == sy ) { //Only apply daylight on square where player is standing to avoid flooding // the lightmap when in less than total sunlight. lm[sx][sy] = natural_light; } } } } // Apply player light sources if (held_luminance > LIGHT_AMBIENT_LOW) { apply_light_source(g->u.posx, g->u.posy, held_luminance, trigdist); } for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { const ter_id terrain = ter(sx, sy); const std::vector<item> &items = i_at(sx, sy); field ¤t_field = field_at(sx, sy); // When underground natural_light is 0, if this changes we need to revisit // Only apply this whole thing if the player is inside, // buildings will be shadowed when outside looking in. if (natural_light > LIGHT_AMBIENT_LOW && !is_outside(g->u.posx, g->u.posy) ) { if (!is_outside(sx, sy)) { // Apply light sources for external/internal divide for(int i = 0; i < 4; ++i) { if (INBOUNDS(sx + dir_x[i], sy + dir_y[i]) && is_outside(sx + dir_x[i], sy + dir_y[i])) { lm[sx][sy] = natural_light; if (light_transparency(sx, sy) > LIGHT_TRANSPARENCY_SOLID) { apply_light_arc(sx, sy, dir_d[i], natural_light); } } } } } for( std::vector<item>::const_iterator itm = items.begin(); itm != items.end(); ++itm ) { float ilum = 0.0; // brightness int iwidth = 0; // 0-360 degrees. 0 is a circular light_source int idir = 0; // otherwise, it's a light_arc pointed in this direction if ( itm->getlight(ilum, iwidth, idir ) ) { if ( iwidth > 0 ) { apply_light_arc( sx, sy, idir, ilum, iwidth ); } else { add_light_source(sx, sy, ilum); } } } if(terrain == t_lava) { add_light_source(sx, sy, 50 ); } if(terrain == t_console) { add_light_source(sx, sy, 3 ); } if(terrain == t_emergency_light) { add_light_source(sx, sy, 3 ); } if(terrain == t_utility_light) { add_light_source(sx, sy, 35 ); } field_entry *cur = NULL; for(std::map<field_id, field_entry *>::iterator field_list_it = current_field.getFieldStart(); field_list_it != current_field.getFieldEnd(); ++field_list_it) { cur = field_list_it->second; if(cur == NULL) { continue; } // TODO: [lightmap] Attach light brightness to fields switch(cur->getFieldType()) { case fd_fire: if (3 == cur->getFieldDensity()) { add_light_source(sx, sy, 160); } else if (2 == cur->getFieldDensity()) { add_light_source(sx, sy, 60); } else { add_light_source(sx, sy, 16); } break; case fd_fire_vent: case fd_flame_burst: add_light_source(sx, sy, 8); break; case fd_electricity: case fd_plasma: if (3 == cur->getFieldDensity()) { add_light_source(sx, sy, 8); } else if (2 == cur->getFieldDensity()) { add_light_source(sx, sy, 1); } else { apply_light_source(sx, sy, LIGHT_SOURCE_LOCAL, trigdist); // kinda a hack as the square will still get marked } break; case fd_incendiary: if (3 == cur->getFieldDensity()) { add_light_source(sx, sy, 30); } else if (2 == cur->getFieldDensity()) { add_light_source(sx, sy, 16); } else { add_light_source(sx, sy, 8); } break; case fd_laser: apply_light_source(sx, sy, 1, trigdist); break; case fd_spotlight: add_light_source(sx, sy, 20); break; case fd_dazzling: add_light_source(sx, sy, 2); break; default: //Suppress warnings break; } } } } for (size_t i = 0; i < g->num_zombies(); ++i) { int mx = g->zombie(i).posx(); int my = g->zombie(i).posy(); if (INBOUNDS(mx, my)) { if (g->zombie(i).has_effect("onfire")) { apply_light_source(mx, my, 3, trigdist); } // TODO: [lightmap] Attach natural light brightness to creatures // TODO: [lightmap] Allow creatures to have light attacks (ie: eyebot) // TODO: [lightmap] Allow creatures to have facing and arc lights if (g->zombie(i).type->luminance > 0) { apply_light_source(mx, my, g->zombie(i).type->luminance, trigdist); } } } // Apply any vehicle light sources VehicleList vehs = get_vehicles(); for( size_t v = 0; v < vehs.size(); ++v ) { if(vehs[v].v->lights_on) { int dir = vehs[v].v->face.dir(); float veh_luminance = 0.0; float iteration = 1.0; std::vector<int> light_indices = vehs[v].v->all_parts_with_feature(VPFLAG_CONE_LIGHT); for (std::vector<int>::iterator part = light_indices.begin(); part != light_indices.end(); ++part) { veh_luminance += ( vehs[v].v->part_info(*part).bonus / iteration ); iteration = iteration * 1.1; } if (veh_luminance > LL_LIT) { for (std::vector<int>::iterator part = light_indices.begin(); part != light_indices.end(); ++part) { int px = vehs[v].x + vehs[v].v->parts[*part].precalc_dx[0]; int py = vehs[v].y + vehs[v].v->parts[*part].precalc_dy[0]; if(INBOUNDS(px, py)) { apply_light_arc(px, py, dir + vehs[v].v->parts[*part].direction, veh_luminance, 45); } } } } if(vehs[v].v->overhead_lights_on) { std::vector<int> light_indices = vehs[v].v->all_parts_with_feature(VPFLAG_CIRCLE_LIGHT); for (std::vector<int>::iterator part = light_indices.begin(); part != light_indices.end(); ++part) { if((calendar::turn % 2 && vehs[v].v->part_info(*part).has_flag(VPFLAG_ODDTURN)) || (!(calendar::turn % 2) && vehs[v].v->part_info(*part).has_flag(VPFLAG_EVENTURN)) || (!vehs[v].v->part_info(*part).has_flag(VPFLAG_EVENTURN) && !vehs[v].v->part_info(*part).has_flag(VPFLAG_ODDTURN))) { int px = vehs[v].x + vehs[v].v->parts[*part].precalc_dx[0]; int py = vehs[v].y + vehs[v].v->parts[*part].precalc_dy[0]; if(INBOUNDS(px, py)) { add_light_source( px, py, vehs[v].v->part_info(*part).bonus ); } } } } } /* Now that we have position and intensity of all bulk light sources, apply_ them This may seem like extra work, but take a 12x12 raging inferno: unbuffered: (12^2)*(160*4) = apply_light_ray x 92160 buffered: (12*4)*(160) = apply_light_ray x 7680 */ for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { if ( light_source_buffer[sx][sy] > 0. ) { apply_light_source(sx, sy, light_source_buffer[sx][sy], ( trigdist && light_source_buffer[sx][sy] > 3. ) ); } } } if (g->u.has_active_bionic("bio_night") ) { for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { if (rl_dist(sx, sy, g->u.posx, g->u.posy) < 15) { lm[sx][sy] = 0; } } } } }
void map::generate_lightmap(game* g) { memset(lm, 0, sizeof(lm)); memset(sm, 0, sizeof(sm)); const int dir_x[] = { 1, 0 , -1, 0 }; const int dir_y[] = { 0, 1 , 0, -1 }; const int dir_d[] = { 180, 270, 0, 90 }; const float held_luminance = g->u.active_light(); const float natural_light = g->natural_light_level(); // Daylight vision handling returned back to map due to issues it causes here if (natural_light > LIGHT_SOURCE_BRIGHT) { // Apply sunlight, first light source so just assign for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { // In bright light indoor light exists to some degree if (!g->m.is_outside(sx, sy)) { lm[sx][sy] = LIGHT_AMBIENT_LOW; } else if (g->u.posx == sx && g->u.posy == sy ) { //Only apply daylight on square where player is standing to avoid flooding // the lightmap when in less than total sunlight. lm[sx][sy] = natural_light; } } } } // Apply player light sources if (held_luminance > LIGHT_AMBIENT_LOW) apply_light_source(g->u.posx, g->u.posy, held_luminance, trigdist); int flood_basalt_check = 0; // does excessive lava need high quality lighting? Nope nope nope nope for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { const ter_id terrain = g->m.ter(sx, sy); const std::vector<item> &items = g->m.i_at(sx, sy); field ¤t_field = g->m.field_at(sx, sy); // When underground natural_light is 0, if this changes we need to revisit if (natural_light > LIGHT_AMBIENT_LOW) { if (!g->m.is_outside(sx, sy)) { // Apply light sources for external/internal divide for(int i = 0; i < 4; ++i) { if (INBOUNDS(sx + dir_x[i], sy + dir_y[i]) && g->m.is_outside(sx + dir_x[i], sy + dir_y[i])) { if (INBOUNDS(sx, sy) && g->m.is_outside(0, 0)) lm[sx][sy] = natural_light; if (g->m.light_transparency(sx, sy) > LIGHT_TRANSPARENCY_SOLID) apply_light_arc(sx, sy, dir_d[i], natural_light); } } } } for( std::vector<item>::const_iterator itm = items.begin(); itm != items.end(); ++itm ) { if ( itm->has_flag("LIGHT_20")) { apply_light_source(sx, sy, 20, trigdist); } if ( itm->has_flag("LIGHT_1")) { apply_light_source(sx, sy, 1, trigdist); } if ( itm->has_flag("LIGHT_4")) { apply_light_source(sx, sy, 4, trigdist); } if ( itm->has_flag("LIGHT_8")) { apply_light_source(sx, sy, 8, trigdist); } } if(terrain == t_lava) { flood_basalt_check++; apply_light_source(sx, sy, 50, trigdist && flood_basalt_check < 512 ); // todo: optimize better } if(terrain == t_console) apply_light_source(sx, sy, 3, false); // 3^2 circle is just silly if(terrain == t_emergency_light) apply_light_source(sx, sy, 3, false); field_entry *cur = NULL; for(std::map<field_id, field_entry*>::iterator field_list_it = current_field.getFieldStart(); field_list_it != current_field.getFieldEnd(); ++field_list_it){ cur = field_list_it->second; if(cur == NULL) continue; // TODO: [lightmap] Attach light brightness to fields switch(cur->getFieldType()) { case fd_fire: if (3 == cur->getFieldDensity()) apply_light_source(sx, sy, 160, trigdist); else if (2 == cur->getFieldDensity()) apply_light_source(sx, sy, 60, trigdist); else apply_light_source(sx, sy, 16, trigdist); break; case fd_fire_vent: case fd_flame_burst: apply_light_source(sx, sy, 8, trigdist); break; case fd_electricity: case fd_plasma: if (3 == cur->getFieldDensity()) apply_light_source(sx, sy, 8, trigdist); else if (2 == cur->getFieldDensity()) apply_light_source(sx, sy, 1, trigdist); else apply_light_source(sx, sy, LIGHT_SOURCE_LOCAL, trigdist); // kinda a hack as the square will still get marked break; case fd_laser: apply_light_source(sx, sy, 1, trigdist); break; } } } } for (int i = 0; i < g->num_zombies(); ++i) { int mx = g->zombie(i).posx(); int my = g->zombie(i).posy(); if (INBOUNDS(mx, my)) { if (g->zombie(i).has_effect(ME_ONFIRE)) { apply_light_source(mx, my, 3, trigdist); } // TODO: [lightmap] Attach natural light brightness to creatures // TODO: [lightmap] Allow creatures to have light attacks (ie: eyebot) // TODO: [lightmap] Allow creatures to have facing and arc lights switch (g->zombie(i).type->id) { case mon_zombie_electric: apply_light_source(mx, my, 1, trigdist); break; case mon_turret: apply_light_source(mx, my, 2, trigdist); break; case mon_flaming_eye: apply_light_source(mx, my, LIGHT_SOURCE_BRIGHT, trigdist); break; case mon_manhack: apply_light_source(mx, my, LIGHT_SOURCE_LOCAL, trigdist); break; } } } // Apply any vehicle light sources VehicleList vehs = g->m.get_vehicles(); for(int v = 0; v < vehs.size(); ++v) { if(vehs[v].v->lights_on) { int dir = vehs[v].v->face.dir(); float veh_luminance=0.0; float iteration=1.0; for (std::vector<int>::iterator part = vehs[v].v->external_parts.begin(); part != vehs[v].v->external_parts.end(); ++part) { int dpart = vehs[v].v->part_with_feature(*part , "LIGHT"); if (dpart >= 0) { veh_luminance += ( vehs[v].v->part_info(dpart).power / iteration ); iteration=iteration * 1.1; } } if (veh_luminance > LL_LIT) { for (std::vector<int>::iterator part = vehs[v].v->external_parts.begin(); part != vehs[v].v->external_parts.end(); ++part) { int px = vehs[v].x + vehs[v].v->parts[*part].precalc_dx[0]; int py = vehs[v].y + vehs[v].v->parts[*part].precalc_dy[0]; if(INBOUNDS(px, py)) { int dpart = vehs[v].v->part_with_feature(*part , "LIGHT"); if (dpart >= 0) { apply_light_arc(px, py, dir + vehs[v].v->parts[dpart].direction, veh_luminance, 45); } } } } } } if (g->u.has_active_bionic("bio_night") ) { for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { if (rl_dist(sx, sy, g->u.posx, g->u.posy) < 15) { lm[sx][sy] = 0; } } } } }