diff --git a/src/cata_tiles.cpp b/src/cata_tiles.cpp index 323836a81981a..0c39342de131d 100644 --- a/src/cata_tiles.cpp +++ b/src/cata_tiles.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include "action.h" #include "avatar.h" @@ -1227,42 +1226,6 @@ void tileset_cache::loader::load_tile_spritelists( const JsonObject &entry, } } -struct tile_render_info { - struct common { - const tripoint pos; - // accumulator for 3d tallness of sprites rendered here so far; - int height_3d = 0; - int draw_min_z = -OVERMAP_DEPTH; - - common( const tripoint &pos, const int height_3d, const int draw_min_z ) - : pos( pos ), height_3d( height_3d ), draw_min_z( draw_min_z ) {} - }; - - struct vision_effect { - visibility_type vis; - - explicit vision_effect( const visibility_type vis ) - : vis( vis ) {} - }; - - struct sprite { - lit_level ll; - std::array invisible; - - sprite( const lit_level ll, const std::array &inv ) - : ll( ll ), invisible( inv ) {} - }; - - common com; - std::variant var; - - tile_render_info( const common &com, const vision_effect &var ) - : com( com ), var( var ) {} - - tile_render_info( const common &com, const sprite &var ) - : com( com ), var( var ) {} -}; - static std::map display_npc_attack_potential() { avatar &you = get_avatar(); @@ -1347,6 +1310,7 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int const point min_visible( you.posx() % SEEX, you.posy() % SEEY ); const point max_visible( ( you.posx() % SEEX ) + ( MAPSIZE - 1 ) * SEEX, ( you.posy() % SEEY ) + ( MAPSIZE - 1 ) * SEEY ); + const int draw_min_z = std::max( you.posz() - max_draw_depth, -OVERMAP_DEPTH ); const level_cache &ch = here.access_cache( center.z ); @@ -1417,20 +1381,6 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int } const point half_tile( tile_width / 2, 0 ); const point quarter_tile( tile_width / 4, tile_height / 4 ); - if( g->display_overlay_state( ACTION_DISPLAY_VEHICLE_AI ) ) { - for( const wrapped_vehicle &elem : here.get_vehicles() ) { - const vehicle &veh = *elem.v; - const point veh_pos = veh.global_pos3().xy(); - for( const auto &overlay_data : veh.get_debug_overlay_data() ) { - const point pt = veh_pos + std::get<0>( overlay_data ); - const int color = std::get<1>( overlay_data ); - const std::string &text = std::get<2>( overlay_data ); - overlay_strings.emplace( player_to_screen( pt ), - formatted_text( text, color, - text_alignment::left ) ); - } - } - } const auto apply_visible = [&]( const tripoint & np, const level_cache & ch, map & here ) { return np.y < min_visible.y || np.y > max_visible.y || np.x < min_visible.x || np.x > max_visible.x || @@ -1446,237 +1396,266 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int } } - creature_tracker &creatures = get_creature_tracker(); - std::map> draw_points; - for( int row = min_row; row < max_row; row ++ ) { - draw_points[row].reserve( std::max( 0, max_col - min_col ) ); - for( int col = min_col; col < max_col; col ++ ) { - const std::optional temp = tile_to_player( { col, row } ); - if( !temp.has_value() ) { - continue; - } - const tripoint pos( temp.value(), center.z ); - const tripoint_abs_ms pos_global = here.getglobal( pos ); - const int &x = pos.x; - const int &y = pos.y; - - // light level is used for choosing between grayscale filter and normal lit tiles. - lit_level ll; - // invisible to normal eyes - std::array invisible; - invisible[0] = false; - - if( y < min_visible.y || y > max_visible.y || x < min_visible.x || x > max_visible.x ) { - if( has_memory_at( pos_global ) ) { - ll = lit_level::MEMORIZED; - invisible[0] = true; - } else if( has_draw_override( pos ) ) { - ll = lit_level::DARK; - invisible[0] = true; - } else { - if( would_apply_vision_effects( offscreen_type ) ) { - draw_points[row].emplace_back( tile_render_info::common{ pos, 0, pos.z }, - tile_render_info::vision_effect{ offscreen_type } ); - } - continue; + if( here.draw_points_cache_dirty ) { + here.draw_points_cache_dirty = false; + // overlay_strings and color_blocks are generated with draw_points and thus are cleared together + here.draw_points_cache.clear(); + here.overlay_strings_cache.clear(); + here.color_blocks_cache = {}; + + if( g->display_overlay_state( ACTION_DISPLAY_VEHICLE_AI ) ) { + for( const wrapped_vehicle &elem : here.get_vehicles() ) { + const vehicle &veh = *elem.v; + const point veh_pos = veh.global_pos3().xy(); + for( const auto &overlay_data : veh.get_debug_overlay_data() ) { + const point pt = veh_pos + std::get<0>( overlay_data ); + const int color = std::get<1>( overlay_data ); + const std::string &text = std::get<2>( overlay_data ); + overlay_strings.emplace( player_to_screen( pt ), + formatted_text( text, color, + text_alignment::left ) ); } - } else { - ll = ch.visibility_cache[x][y]; } + } - // Add scent value to the overlay_strings list for every visible tile when - // displaying scent - if( g->display_overlay_state( ACTION_DISPLAY_SCENT ) && !invisible[0] ) { - const int scent_value = get_scent().get( pos ); - if( scent_value > 0 ) { - overlay_strings.emplace( player_to_screen( point( x, y ) ) + half_tile, - formatted_text( std::to_string( scent_value ), - 8 + catacurses::yellow, direction::NORTH ) ); - } + // Generate new draw points + creature_tracker &creatures = get_creature_tracker(); + for( int row = min_row; row < max_row; row ++ ) { + // Reserve columns on each row + for( int zlevel = center.z; zlevel >= draw_min_z; zlevel -- ) { + here.draw_points_cache[zlevel][row].reserve( std::max( 0, max_col - min_col ) ); } - - // Add scent type to the overlay_strings list for every visible tile when - // displaying scent - if( g->display_overlay_state( ACTION_DISPLAY_SCENT_TYPE ) && !invisible[0] ) { - const scenttype_id scent_type = get_scent().get_type( pos ); - if( !scent_type.is_empty() ) { - overlay_strings.emplace( player_to_screen( point( x, y ) ) + half_tile, - formatted_text( scent_type.c_str(), - 8 + catacurses::yellow, direction::NORTH ) ); + for( int col = min_col; col < max_col; col ++ ) { + const std::optional temp = tile_to_player( { col, row } ); + if( !temp.has_value() ) { + continue; } - } - - if( g->display_overlay_state( ACTION_DISPLAY_RADIATION ) ) { - const auto rad_override = radiation_override.find( pos ); - const bool rad_overridden = rad_override != radiation_override.end(); - if( rad_overridden || !invisible[0] ) { - const int rad_value = rad_overridden ? rad_override->second : - here.get_radiation( pos ); - catacurses::base_color col; - if( rad_value > 0 ) { - col = catacurses::green; + for( int zlevel = center.z; zlevel >= draw_min_z; zlevel -- ) { + const tripoint pos( temp.value(), zlevel ); + const tripoint_abs_ms pos_global = here.getglobal( pos ); + const int &x = pos.x; + const int &y = pos.y; + const bool is_center_z = zlevel == center.z; + const level_cache &ch2 = here.access_cache( zlevel ); + + // light level is used for choosing between grayscale filter and normal lit tiles. + lit_level ll; + // invisible to normal eyes + std::array invisible; + invisible[0] = false; + + if( y < min_visible.y || y > max_visible.y || x < min_visible.x || x > max_visible.x ) { + if( has_memory_at( pos_global ) ) { + ll = lit_level::MEMORIZED; + invisible[0] = true; + } else if( has_draw_override( pos ) ) { + ll = lit_level::DARK; + invisible[0] = true; + } else { + if( would_apply_vision_effects( offscreen_type ) ) { + here.draw_points_cache[zlevel][row].emplace_back( tile_render_info::common{ pos, 0 }, + tile_render_info::vision_effect{ offscreen_type } ); + } + break; + } } else { - col = catacurses::cyan; + ll = ch2.visibility_cache[x][y]; } - overlay_strings.emplace( player_to_screen( point( x, y ) ) + half_tile, - formatted_text( std::to_string( rad_value ), - 8 + col, direction::NORTH ) ); - } - } - if( g->display_overlay_state( ACTION_DISPLAY_NPC_ATTACK_POTENTIAL ) ) { - if( npc_attack_rating_map.count( pos ) ) { - const int val = npc_attack_rating_map.at( pos ); - short color; - if( val <= 0 ) { - color = catacurses::red; - } else if( val == max_npc_effectiveness ) { - color = catacurses::cyan; - } else { - color = catacurses::white; - } - overlay_strings.emplace( player_to_screen( point( x, y ) ) + half_tile, - formatted_text( std::to_string( val ), color, - direction::NORTH ) ); - } - } + if( is_center_z ) { + // Add scent value to the overlay_strings list for every visible tile when + // displaying scent + if( g->display_overlay_state( ACTION_DISPLAY_SCENT ) && !invisible[0] ) { + const int scent_value = get_scent().get( pos ); + if( scent_value > 0 ) { + here.overlay_strings_cache.emplace( player_to_screen( point( x, y ) ) + half_tile, + formatted_text( std::to_string( scent_value ), + 8 + catacurses::yellow, direction::NORTH ) ); + } + } - // Add temperature value to the overlay_strings list for every visible tile when - // displaying temperature - if( g->display_overlay_state( ACTION_DISPLAY_TEMPERATURE ) && !invisible[0] ) { - const units::temperature temp_value = get_weather().get_temperature( pos ); - const float celsius_temp_value = units::to_celsius( temp_value ); - short color; - const short bold = 8; - if( celsius_temp_value > 40 ) { - color = catacurses::red; - } else if( celsius_temp_value > 25 ) { - color = catacurses::yellow + bold; - } else if( celsius_temp_value > 10 ) { - color = catacurses::green + bold; - } else if( celsius_temp_value > 0 ) { - color = catacurses::white + bold; - } else if( celsius_temp_value > -10 ) { - color = catacurses::cyan + bold; - } else { - color = catacurses::blue + bold; - } + // Add scent type to the overlay_strings list for every visible tile when + // displaying scent + if( g->display_overlay_state( ACTION_DISPLAY_SCENT_TYPE ) && !invisible[0] ) { + const scenttype_id scent_type = get_scent().get_type( pos ); + if( !scent_type.is_empty() ) { + here.overlay_strings_cache.emplace( player_to_screen( point( x, y ) ) + half_tile, + formatted_text( scent_type.c_str(), + 8 + catacurses::yellow, direction::NORTH ) ); + } + } - std::string temp_str; - if( get_option( "USE_CELSIUS" ) == "celsius" ) { - temp_str = string_format( "%.0f", celsius_temp_value ); - } else if( get_option( "USE_CELSIUS" ) == "kelvin" ) { - temp_str = string_format( "%.0f", units::to_kelvin( temp_value ) ); - } else { - temp_str = string_format( "%.0f", units::to_fahrenheit( temp_value ) ); - } - overlay_strings.emplace( player_to_screen( point( x, y ) ), - formatted_text( temp_str, color, - text_alignment::left ) ); - } + if( g->display_overlay_state( ACTION_DISPLAY_RADIATION ) ) { + const auto rad_override = radiation_override.find( pos ); + const bool rad_overridden = rad_override != radiation_override.end(); + if( rad_overridden || !invisible[0] ) { + const int rad_value = rad_overridden ? rad_override->second : + here.get_radiation( pos ); + catacurses::base_color col; + if( rad_value > 0 ) { + col = catacurses::green; + } else { + col = catacurses::cyan; + } + here.overlay_strings_cache.emplace( player_to_screen( point( x, y ) ) + half_tile, + formatted_text( std::to_string( rad_value ), + 8 + col, direction::NORTH ) ); + } + } - if( g->display_overlay_state( ACTION_DISPLAY_VISIBILITY ) && - g->displaying_visibility_creature && !invisible[0] ) { - const bool visibility = g->displaying_visibility_creature->sees( pos ); - - // color overlay. - SDL_Color block_color = visibility ? windowsPalette[catacurses::green] : - SDL_Color{ 192, 192, 192, 255 }; - block_color.a = 100; - color_blocks.first = SDL_BLENDMODE_BLEND; - color_blocks.second.emplace( player_to_screen( point( x, y ) ), block_color ); - - // overlay string - std::string visibility_str = visibility ? "+" : "-"; - overlay_strings.emplace( player_to_screen( point( x, y ) ) + quarter_tile, - formatted_text( visibility_str, catacurses::black, - direction::NORTH ) ); - } + if( g->display_overlay_state( ACTION_DISPLAY_NPC_ATTACK_POTENTIAL ) ) { + if( npc_attack_rating_map.count( pos ) ) { + const int val = npc_attack_rating_map.at( pos ); + short color; + if( val <= 0 ) { + color = catacurses::red; + } else if( val == max_npc_effectiveness ) { + color = catacurses::cyan; + } else { + color = catacurses::white; + } + here.overlay_strings_cache.emplace( player_to_screen( point( x, y ) ) + half_tile, + formatted_text( std::to_string( val ), color, + direction::NORTH ) ); + } + } - static std::vector lighting_colors; - // color hue in the range of [0..10], 0 being white, 10 being blue - auto draw_debug_tile = [&]( const int color_hue, const std::string & text ) { - if( lighting_colors.empty() ) { - SDL_Color white = { 255, 255, 255, 255 }; - SDL_Color blue = { 0, 0, 255, 255 }; - lighting_colors = color_linear_interpolate( white, blue, 9 ); - } - point tile_pos = player_to_screen( point( x, y ) ); + // Add temperature value to the overlay_strings list for every visible tile when + // displaying temperature + if( g->display_overlay_state( ACTION_DISPLAY_TEMPERATURE ) && !invisible[0] ) { + const units::temperature temp_value = get_weather().get_temperature( pos ); + const float celsius_temp_value = units::to_celsius( temp_value ); + short color; + const short bold = 8; + if( celsius_temp_value > 40 ) { + color = catacurses::red; + } else if( celsius_temp_value > 25 ) { + color = catacurses::yellow + bold; + } else if( celsius_temp_value > 10 ) { + color = catacurses::green + bold; + } else if( celsius_temp_value > 0 ) { + color = catacurses::white + bold; + } else if( celsius_temp_value > -10 ) { + color = catacurses::cyan + bold; + } else { + color = catacurses::blue + bold; + } - // color overlay - SDL_Color color = lighting_colors[std::min( std::max( 0, color_hue ), 10 )]; - color.a = 100; - color_blocks.first = SDL_BLENDMODE_BLEND; - color_blocks.second.emplace( tile_pos, color ); + std::string temp_str; + if( get_option( "USE_CELSIUS" ) == "celsius" ) { + temp_str = string_format( "%.0f", celsius_temp_value ); + } else if( get_option( "USE_CELSIUS" ) == "kelvin" ) { + temp_str = string_format( "%.0f", units::to_kelvin( temp_value ) ); + } else { + temp_str = string_format( "%.0f", units::to_fahrenheit( temp_value ) ); + } + here.overlay_strings_cache.emplace( player_to_screen( point( x, y ) ), + formatted_text( temp_str, color, + text_alignment::left ) ); + } - // string overlay - overlay_strings.emplace( - tile_pos + quarter_tile, - formatted_text( text, catacurses::black, direction::NORTH ) ); - }; + if( g->display_overlay_state( ACTION_DISPLAY_VISIBILITY ) && + g->displaying_visibility_creature && !invisible[0] ) { + const bool visibility = g->displaying_visibility_creature->sees( pos ); + + // color overlay. + SDL_Color block_color = visibility ? windowsPalette[catacurses::green] : + SDL_Color{ 192, 192, 192, 255 }; + block_color.a = 100; + here.color_blocks_cache.first = SDL_BLENDMODE_BLEND; + here.color_blocks_cache.second.emplace( player_to_screen( point( x, y ) ), block_color ); + + // overlay string + std::string visibility_str = visibility ? "+" : "-"; + here.overlay_strings_cache.emplace( player_to_screen( point( x, y ) ) + quarter_tile, + formatted_text( visibility_str, catacurses::black, + direction::NORTH ) ); + } - if( g->display_overlay_state( ACTION_DISPLAY_LIGHTING ) ) { - if( g->displaying_lighting_condition == 0 ) { - const float light = here.ambient_light_at( {x, y, center.z} ); - // note: lighting will be constrained in the [1.0, 11.0] range. - int intensity = - static_cast( std::max( 1.0, LIGHT_AMBIENT_LIT - light + 1.0 ) ) - 1; - draw_debug_tile( intensity, string_format( "%.1f", light ) ); - } - } + static std::vector lighting_colors; + // color hue in the range of [0..10], 0 being white, 10 being blue + auto draw_debug_tile = [&]( const int color_hue, const std::string & text ) { + if( lighting_colors.empty() ) { + SDL_Color white = { 255, 255, 255, 255 }; + SDL_Color blue = { 0, 0, 255, 255 }; + lighting_colors = color_linear_interpolate( white, blue, 9 ); + } + point tile_pos = player_to_screen( point( x, y ) ); + + // color overlay + SDL_Color color = lighting_colors[std::min( std::max( 0, color_hue ), 10 )]; + color.a = 100; + here.color_blocks_cache.first = SDL_BLENDMODE_BLEND; + here.color_blocks_cache.second.emplace( tile_pos, color ); + + // string overlay + here.overlay_strings_cache.emplace( + tile_pos + quarter_tile, + formatted_text( text, catacurses::black, direction::NORTH ) ); + }; + + if( g->display_overlay_state( ACTION_DISPLAY_LIGHTING ) ) { + if( g->displaying_lighting_condition == 0 ) { + const float light = here.ambient_light_at( {x, y, center.z} ); + // note: lighting will be constrained in the [1.0, 11.0] range. + int intensity = + static_cast( std::max( 1.0, LIGHT_AMBIENT_LIT - light + 1.0 ) ) - 1; + draw_debug_tile( intensity, string_format( "%.1f", light ) ); + } + } - if( g->display_overlay_state( ACTION_DISPLAY_TRANSPARENCY ) ) { - const float tr = here.light_transparency( {x, y, center.z} ); - int intensity = tr <= LIGHT_TRANSPARENCY_SOLID ? 10 : static_cast - ( ( tr - LIGHT_TRANSPARENCY_OPEN_AIR ) * 8 ); - draw_debug_tile( intensity, string_format( "%.2f", tr ) ); - } + if( g->display_overlay_state( ACTION_DISPLAY_TRANSPARENCY ) ) { + const float tr = here.light_transparency( {x, y, center.z} ); + int intensity = tr <= LIGHT_TRANSPARENCY_SOLID ? 10 : static_cast + ( ( tr - LIGHT_TRANSPARENCY_OPEN_AIR ) * 8 ); + draw_debug_tile( intensity, string_format( "%.2f", tr ) ); + } - if( g->display_overlay_state( ACTION_DISPLAY_REACHABILITY_ZONES ) ) { - tripoint tile_pos( x, y, center.z ); - int value = here.reachability_cache_value( tile_pos, - g->debug_rz_display.r_cache_vertical, g->debug_rz_display.quadrant ); - // use color to denote reachability from you to the target tile according to the - // cache - bool reachable = here.has_potential_los( you.pos(), tile_pos ); - draw_debug_tile( reachable ? 0 : 6, std::to_string( value ) ); - } + if( g->display_overlay_state( ACTION_DISPLAY_REACHABILITY_ZONES ) ) { + tripoint tile_pos( x, y, center.z ); + int value = here.reachability_cache_value( tile_pos, + g->debug_rz_display.r_cache_vertical, g->debug_rz_display.quadrant ); + // use color to denote reachability from you to the target tile according to the + // cache + bool reachable = here.has_potential_los( you.pos(), tile_pos ); + draw_debug_tile( reachable ? 0 : 6, std::to_string( value ) ); + } + } - if( !invisible[0] ) { - const visibility_type vis_type = here.get_visibility( ll, cache ); - if( would_apply_vision_effects( vis_type ) ) { - const Creature *critter = creatures.creature_at( pos, true ); - if( has_draw_override( pos ) || has_memory_at( pos_global ) || - ( critter && - ( critter->has_flag( mon_flag_ALWAYS_VISIBLE ) - || you.sees_with_infrared( *critter ) - || you.sees_with_specials( *critter ) ) ) ) { - invisible[0] = true; - } else { - draw_points[row].emplace_back( tile_render_info::common{ pos, 0, pos.z }, - tile_render_info::vision_effect{ vis_type } ); - continue; + if( !invisible[0] ) { + const visibility_type vis_type = here.get_visibility( ll, cache ); + if( would_apply_vision_effects( vis_type ) ) { + const Creature *critter = creatures.creature_at( pos, true ); + if( has_draw_override( pos ) || has_memory_at( pos_global ) || + ( critter && + ( critter->has_flag( mon_flag_ALWAYS_VISIBLE ) + || you.sees_with_infrared( *critter ) + || you.sees_with_specials( *critter ) ) ) ) { + invisible[0] = true; + } else { + here.draw_points_cache[zlevel][row].emplace_back( tile_render_info::common{ pos, 0 }, + tile_render_info::vision_effect{ vis_type } ); + break; + } + } + } + for( int i = 0; i < 4; i++ ) { + const tripoint np = pos + neighborhood[i]; + invisible[1 + i] = apply_visible( np, ch2, here ); } - } - } - for( int i = 0; i < 4; i++ ) { - const tripoint np = pos + neighborhood[i]; - invisible[1 + i] = apply_visible( np, ch, here ); - } - // Determine lowest z-level to draw for 3D vision. Some off-screen - // tiles are also considered here to simply the logic. - int draw_min_z; - tripoint p_temp = pos; - while( !here.dont_draw_lower_floor( p_temp ) && pos.z - p_temp.z < max_draw_depth ) { - p_temp.z -= 1; + here.draw_points_cache[zlevel][row].emplace_back( tile_render_info::common{ pos, 0 }, + tile_render_info::sprite{ ll, invisible } ); + // Stop building draw points below when floor reached + if( here.dont_draw_lower_floor( pos ) ) { + break; + } + } } - draw_min_z = p_temp.z; - - draw_points[row].emplace_back( tile_render_info::common{ pos, 0, draw_min_z }, - tile_render_info::sprite{ ll, invisible } ); } } + overlay_strings = here.overlay_strings_cache; + color_blocks = here.color_blocks_cache; // List all layers for a single z-level const std::array drawing_layers = {{ @@ -1709,7 +1688,7 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int // Legacy draw mode for( int row = min_row; row < max_row; row ++ ) { for( auto f : drawing_layers_legacy ) { - for( tile_render_info &p : draw_points[row] ) { + for( tile_render_info &p : here.draw_points_cache[center.z][row] ) { if( const tile_render_info::vision_effect * const var = std::get_if( &p.var ) ) { if( f == &cata_tiles::draw_terrain ) { @@ -1726,86 +1705,51 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int // Multi z-level draw mode // Start drawing from the lowest visible z-level (some off-screen tiles // are considered visible here to simplify the logic.) - int cur_zlevel = center.z + 1; - for( const std::pair> &pts : draw_points ) { - for( const tile_render_info &p : pts.second ) { - cur_zlevel = std::min( cur_zlevel, p.com.draw_min_z ); - if( cur_zlevel <= center.z - max_draw_depth - || cur_zlevel <= -OVERMAP_DEPTH ) { - break; - } - } - } + int cur_zlevel = draw_min_z; while( cur_zlevel <= center.z ) { const half_open_rectangle &cur_any_tile_range = is_isometric() ? z_any_tile_range[center.z - cur_zlevel] : top_any_tile_range; // For each row for( int row = cur_any_tile_range.p_min.y; row < cur_any_tile_range.p_max.y; row ++ ) { // Set base height for each tile - for( tile_render_info &p : draw_points[row] ) { + for( tile_render_info &p : here.draw_points_cache[cur_zlevel][row] ) { p.com.height_3d = ( cur_zlevel - center.z ) * zlevel_height; } // For each layer for( auto f : drawing_layers ) { // For each tile - for( tile_render_info &p : draw_points[row] ) { - // Skip if z-level less than draw_min_z - // Basically occlusion culling - if( cur_zlevel < p.com.draw_min_z ) { - continue; - } - tripoint draw_loc = p.com.pos; - draw_loc.z = cur_zlevel; + for( tile_render_info &p : here.draw_points_cache[cur_zlevel][row] ) { if( const tile_render_info::vision_effect * const var = std::get_if( &p.var ) ) { if( f == &cata_tiles::draw_terrain ) { - apply_vision_effects( draw_loc, var->vis, p.com.height_3d ); + apply_vision_effects( p.com.pos, var->vis, p.com.height_3d ); } } else if( const tile_render_info::sprite * const var = std::get_if( &p.var ) ) { // Get visibility variables - lit_level ll; - std::array invisible; - if( cur_zlevel == center.z ) { - // For the same z-level, use tile_render_info vars - ll = var->ll; - invisible = var->invisible; - } else { - // Otherwise, recalculate ll and invisible - if( here.ll_invis_cache.count( draw_loc ) == 0 ) { - const std::pair> ll_invis = calc_ll_invis( draw_loc ); - ll = ll_invis.first; - invisible = ll_invis.second; - // Only cache ll_invis if not in test mode - if( !test_mode ) { - here.ll_invis_cache[ draw_loc ] = ll_invis; - } - } else { - ll = here.ll_invis_cache[ draw_loc ].first; - invisible = here.ll_invis_cache[ draw_loc ].second; - } - } + lit_level ll = var->ll; + std::array invisible = var->invisible; if( f == &cata_tiles::draw_vpart_no_roof || f == &cata_tiles::draw_vpart_roof ) { int temp_height_3d = p.com.height_3d; // Reset height_3d to base when drawing vehicles p.com.height_3d = ( cur_zlevel - center.z ) * zlevel_height; // Draw - if( !( this->*f )( draw_loc, ll, p.com.height_3d, invisible, false ) ) { + if( !( this->*f )( p.com.pos, ll, p.com.height_3d, invisible, false ) ) { // If no vpart drawn, revert height_3d changes p.com.height_3d = temp_height_3d; } } else if( f == &cata_tiles::draw_critter_at ) { // Draw - if( !( this->*f )( draw_loc, ll, p.com.height_3d, invisible, false ) && do_draw_shadow && - cur_zlevel == p.com.draw_min_z ) { + if( !( this->*f )( p.com.pos, ll, p.com.height_3d, invisible, false ) && do_draw_shadow && + here.dont_draw_lower_floor( p.com.pos ) ) { // Draw shadow of flying critters on bottom-most tile if no other critter drawn - draw_critter_above( draw_loc, ll, p.com.height_3d, invisible ); + draw_critter_above( p.com.pos, ll, p.com.height_3d, invisible ); } } else { // Draw - ( this->*f )( draw_loc, ll, p.com.height_3d, invisible, false ); + ( this->*f )( p.com.pos, ll, p.com.height_3d, invisible, false ); } } } @@ -1817,7 +1761,7 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int // display number of monsters to spawn in mapgen preview for( int row = top_any_tile_range.p_min.y; row < top_any_tile_range.p_max.y; row ++ ) { - for( const tile_render_info &p : draw_points[row] ) { + for( const tile_render_info &p : here.draw_points_cache[center.z][row] ) { const tile_render_info::sprite *const var = std::get_if( &p.var ); if( !var ) { @@ -1957,68 +1901,9 @@ void cata_tiles::draw( const point &dest, const tripoint ¢er, int width, int "SDL_RenderSetClipRect failed" ); } -std::pair> cata_tiles::calc_ll_invis( const tripoint &draw_loc ) -{ - avatar &you = get_avatar(); - map &here = get_map(); - creature_tracker &creatures = get_creature_tracker(); - const visibility_variables &cache = here.get_visibility_variables_cache(); - const point min_visible( you.posx() % SEEX, you.posy() % SEEY ); - const point max_visible( ( you.posx() % SEEX ) + ( MAPSIZE - 1 ) * SEEX, - ( you.posy() % SEEY ) + ( MAPSIZE - 1 ) * SEEY ); - const level_cache &ch = here.access_cache( draw_loc.z ); - const auto apply_visible = [&]( const tripoint & np, const level_cache & ch, map & here ) { - return np.y < min_visible.y || np.y > max_visible.y || - np.x < min_visible.x || np.x > max_visible.x || - would_apply_vision_effects( here.get_visibility( ch.visibility_cache[np.x][np.y], - cache ) ); - }; - - lit_level ll = lit_level::DARK; - // invisible to normal eyes - std::array invisible; - invisible[0] = false; - tripoint pos = draw_loc; - tripoint_abs_ms pos_global = here.getglobal( pos ); - - if( draw_loc.y < min_visible.y || draw_loc.y > max_visible.y || draw_loc.x < min_visible.x || - draw_loc.x > max_visible.x ) { - if( has_memory_at( pos_global ) ) { - ll = lit_level::MEMORIZED; - invisible[0] = true; - } else if( has_draw_override( pos ) ) { - ll = lit_level::DARK; - invisible[0] = true; - } - } else { - ll = here.access_cache( draw_loc.z ).visibility_cache[draw_loc.x][draw_loc.y]; - } - - if( !invisible[0] ) { - const visibility_type vis_type = here.get_visibility( ll, cache ); - if( would_apply_vision_effects( vis_type ) ) { - const Creature *critter = creatures.creature_at( pos, true ); - if( has_draw_override( pos ) || has_memory_at( pos_global ) || - ( critter && - ( critter->has_flag( mon_flag_ALWAYS_VISIBLE ) - || you.sees_with_infrared( *critter ) - || you.sees_with_specials( *critter ) ) ) ) { - invisible[0] = true; - } - } - } - for( int i = 0; i < 4; i++ ) { - const tripoint np = pos + neighborhood[i]; - invisible[1 + i] = apply_visible( np, ch, here ); - } - - std::pair> ret( ll, invisible ); - return ret; -} - -void cata_tiles::clear_draw_caches() +void cata_tiles::set_draw_cache_dirty() { - get_map().ll_invis_cache.clear(); + get_map().draw_points_cache_dirty = true; } void cata_tiles::draw_minimap( const point &dest, const tripoint ¢er, int width, int height ) @@ -3062,7 +2947,7 @@ bool cata_tiles::draw_sprite_at( printErrorIf( ret != 0, "SDL_RenderCopyEx() failed" ); // this reference passes all the way back up the call chain back to - // cata_tiles::draw() draw_points[row][col].com.height_3d + // cata_tiles::draw() here.draw_points_cache[z][row][col].com.height_3d // where we are accumulating the height of every sprite stacked up in a tile height_3d += tile.height_3d; return true; diff --git a/src/cata_tiles.h b/src/cata_tiles.h index b4ea1ab2458aa..730749dabed69 100644 --- a/src/cata_tiles.h +++ b/src/cata_tiles.h @@ -411,9 +411,6 @@ class cata_tiles void draw( const point &dest, const tripoint ¢er, int width, int height, std::multimap &overlay_strings, color_block_overlay_container &color_blocks ); - // Standalone version of the ll and invisible calculations normally done when accumulating draw_points - // Used to determine visibility of lower z-levels in 3D vision without generating extra draw_points and overlay_strings - std::pair> calc_ll_invis( const tripoint &draw_loc ); void draw_om( const point &dest, const tripoint_abs_omt ¢er_abs_omt, bool blink ); /** Minimap functionality */ @@ -808,11 +805,8 @@ class cata_tiles pimpl minimap; public: - // Draw caches persist data between draws to avoid unnecessary recalculations - // Any event that would invalidate cached data should also clear it - // Currently only includes ll_invis_cache - // Performance gain from caching draw_points, overlay_strings and color_blocks is negligible - void clear_draw_caches(); + // Draw caches persist data between draws and are only recalculated when dirty + void set_draw_cache_dirty(); std::string memory_map_mode = "color_pixel_sepia"; }; diff --git a/src/handle_action.cpp b/src/handle_action.cpp index 3140283ab0279..4d303deb35580 100644 --- a/src/handle_action.cpp +++ b/src/handle_action.cpp @@ -263,6 +263,10 @@ input_context game::get_player_input( std::string &action ) const visibility_variables &cache = m.get_visibility_variables_cache(); const level_cache &map_cache = m.get_cache_ref( u.posz() ); const auto &visibility_cache = map_cache.visibility_cache; +#if defined(TILES) + // Mark cata_tiles draw caches as dirty + tilecontext->set_draw_cache_dirty(); +#endif user_turn current_turn; diff --git a/src/map.cpp b/src/map.cpp index 10cc9a4bc03a6..e169d43d51283 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -6695,8 +6695,8 @@ void map::update_visibility_cache( const int zlev ) } #if defined(TILES) - // clear previously cached visibility variables from cata_tiles - tilecontext->clear_draw_caches(); + // Mark cata_tiles draw caches as dirty + tilecontext->set_draw_cache_dirty(); #endif visibility_variables_cache.last_pos = player_character.pos(); @@ -9399,6 +9399,10 @@ void map::build_map_cache( const int zlev, bool skip_lightmap ) player_prev_pos = p; player_prev_range = sr; camera_cache_dirty = true; +#if defined(TILES) + // Mark cata_tiles draw caches as dirty + tilecontext->set_draw_cache_dirty(); +#endif } if( camera_cache_dirty ) { u.moncam_cache = mcache; diff --git a/src/map.h b/src/map.h index 39eb15545c3f2..185a04ea69b4d 100644 --- a/src/map.h +++ b/src/map.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "calendar.h" @@ -45,6 +46,10 @@ #include "units.h" #include "value_ptr.h" +#if defined(TILES) +#include "cata_tiles.h" +#endif + struct scent_block; namespace catacurses @@ -276,6 +281,41 @@ struct drawsq_params { //@} }; +struct tile_render_info { + struct common { + const tripoint pos; + // accumulator for 3d tallness of sprites rendered here so far; + int height_3d = 0; + + common( const tripoint &pos, const int height_3d ) + : pos( pos ), height_3d( height_3d ) {} + }; + + struct vision_effect { + visibility_type vis; + + explicit vision_effect( const visibility_type vis ) + : vis( vis ) {} + }; + + struct sprite { + lit_level ll; + std::array invisible; + + sprite( const lit_level ll, const std::array &inv ) + : ll( ll ), invisible( inv ) {} + }; + + common com; + std::variant var; + + tile_render_info( const common &com, const vision_effect &var ) + : com( com ), var( var ) {} + + tile_render_info( const common &com, const sprite &var ) + : com( com ), var( var ) {} +}; + /** * Manage and cache data about a part of the map. * @@ -2309,7 +2349,12 @@ class map bool has_haulable_items( const tripoint &pos ); std::vector get_haulable_items( const tripoint &pos ); - std::map>> ll_invis_cache; +#if defined(TILES) + bool draw_points_cache_dirty = true; + std::map>> draw_points_cache; + std::multimap overlay_strings_cache; + color_block_overlay_container color_blocks_cache; +#endif }; map &get_map();