diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 151d9a50..ed6a37c9 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -15,28 +15,30 @@ SETUP_LOGGER void printHelp(char *binary) { logger::info( "Usage: {} WORLDPATH\n\n" - " -from X Z coordinates of the block to start rendering at\n" - " -to X Z coordinates of the block to stop rendering at\n" - " -min/max VAL minimum/maximum Y index of blocks to render\n" - " -file NAME output file; default is 'output.png'\n" - " -colors NAME color file to use; default is 'colors.json'\n" - " -nw -ne -se -sw the orientation of the map\n" - " -nether render the nether\n" - " -end render the end\n" - " -dim[ension] NAME render a dimension by namespaced ID\n" - " -nowater do not render water\n" - " -nobeacons do not render beacon beams\n" - " -shading toggle shading (brightens depending on height)\n" - " -lighting toggle lighting (brightens depending on light)\n" - " -mb int (=3500) use the specified amount of memory (in MB)\n" - " -tile int (=1024) render terrain in tiles of the specified size\n" - " -marker X Z color draw a marker at X Z of the desired color\n" - " -padding int (=5) padding to use around the image\n" - " -h[elp] display an option summary\n" - " -v[erbose] toggle debug mode (-vv for more)\n" - " -dumpcolors dump a json with all defined colors\n" - " -radius VAL radius of the circular render\n" - " -centre|-center X Z coordinates of the centre of circular render\n", + " -from X Z coordinates of the block to start rendering at\n" + " -to X Z coordinates of the block to stop rendering at\n" + " -min/max VAL minimum/maximum Y index of blocks to render\n" + " -file NAME output file; default is 'output.png'\n" + " -colors NAME color file to use; default is 'colors.json'\n" + " -nw -ne -se -sw the orientation of the map\n" + " -nether render the nether\n" + " -end render the end\n" + " -dim[ension] NAME render a dimension by namespaced ID\n" + " -nowater do not render water\n" + " -nobeacons do not render beacon beams\n" + " -shading toggle shading (brightens depending on height)\n" + " -lighting toggle lighting (brightens depending on light)\n" + " -mb int (=3500) use the specified amount of memory (in MB)\n" + " -fragment int (=1024) render terrain in tiles of the specified size\n" + " -tile int (=0) if not 0, will create split output of the " + "desired tile size\n" + " -marker X Z color draw a marker at X Z of the desired color\n" + " -padding int (=5) padding to use around the image\n" + " -h[elp] display an option summary\n" + " -v[erbose] toggle debug mode (-vv for more)\n" + " -dumpcolors dump a json with all defined colors\n" + " -radius VAL radius of the circular render\n" + " -centre|-center X Z coordinates of the centre of circular render\n", binary); } diff --git a/src/include/map.hpp b/src/include/map.hpp index daada82b..b180acab 100644 --- a/src/include/map.hpp +++ b/src/include/map.hpp @@ -121,7 +121,8 @@ struct Coordinates { #undef BYTESPERPIXEL } - void tile(std::vector> &fragments, size_t size) const { + void fragment(std::vector> &fragments, + size_t size) const { for (Integer x = minX; x <= maxX; x += size) { for (Integer z = minZ; z <= maxZ; z += size) { Coordinates fragment = *this; diff --git a/src/mcmap.cpp b/src/mcmap.cpp index 58ecb9e2..1ad282fb 100644 --- a/src/mcmap.cpp +++ b/src/mcmap.cpp @@ -14,27 +14,29 @@ int render(const Settings::WorldOptions &options, const Colors::Palette &colors, logger::debug("Rendering {} with {}\n", options.save.name, options.boundaries.to_string()); - // Split terrain according to tile size - std::vector tiles; - options.boundaries.tile(tiles, options.tile_size); + // Divide terrain according to fragment size + std::vector fragment_coordinates; + options.boundaries.fragment(fragment_coordinates, options.fragment_size); - std::vector fragments(tiles.size()); + std::vector fragments(fragment_coordinates.size()); - Progress::Status s = Progress::Status(tiles.size(), cb, Progress::RENDERING); + Progress::Status s = + Progress::Status(fragment_coordinates.size(), cb, Progress::RENDERING); // This value represents the amount of canvasses that can fit in memory at // once to avoid going over the limit of RAM - Counter capacity = memory_capacity( - options.mem_limit, tiles[0].footprint(), tiles.size(), THREADS); + Counter capacity = + memory_capacity(options.mem_limit, fragment_coordinates[0].footprint(), + fragment_coordinates.size(), THREADS); if (!capacity) return false; - logger::debug("Memory capacity: {} tiles - {} tiles scheduled\n", - size_t(capacity), tiles.size()); + logger::debug("Memory capacity: {} fragments - {} fragments scheduled\n", + size_t(capacity), fragment_coordinates.size()); // If caching is needed, ensure the cache directory is available - if (capacity < tiles.size()) + if (capacity < fragments.size()) if (!prepare_cache(getTempDir())) return false; @@ -46,14 +48,14 @@ int render(const Settings::WorldOptions &options, const Colors::Palette &colors, #ifdef _OPENMP #pragma omp for ordered schedule(dynamic) #endif - for (OMP_FOR_INDEX i = 0; i < tiles.size(); i++) { - logger::debug("Rendering {}\n", tiles[i].to_string()); + for (OMP_FOR_INDEX i = 0; i < fragment_coordinates.size(); i++) { + logger::debug("Rendering {}\n", fragment_coordinates[i].to_string()); IsometricCanvas canvas; - canvas.setMap(tiles[i]); + canvas.setMap(fragment_coordinates[i]); canvas.setColors(colors); // Load the minecraft terrain to render - Terrain::Data world(tiles[i], options.regionDir(), colors); + Terrain::Data world(fragment_coordinates[i], options.regionDir(), colors); // Draw the terrain fragment canvas.shading = options.shading; @@ -99,10 +101,20 @@ int render(const Settings::WorldOptions &options, const Colors::Palette &colors, } begin = std::chrono::high_resolution_clock::now(); - if (!merged.save(options.outFile, options.padding, cb)) - return false; + + bool save_status; + + if (options.tile_size) { + save_status = merged.tile(options.outFile, options.tile_size, cb); + } else { + save_status = merged.save(options.outFile, options.padding, cb); + } + end = std::chrono::high_resolution_clock::now(); + if (!save_status) + return false; + logger::debug( "Drawn PNG in {}ms\n", std::chrono::duration_cast(end - begin) diff --git a/src/settings.cpp b/src/settings.cpp index 770c32a0..010fa3c7 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -125,6 +125,12 @@ bool Settings::parseArgs(int argc, char **argv, Settings::WorldOptions *opts) { return false; } opts->tile_size = atoi(NEXTARG); + } else if (strcmp(option, "-fragment") == 0) { + if (!MOREARGS(1) || !isNumeric(POLLARG(1))) { + logger::error("{} needs an integer\n", option); + return false; + } + opts->fragment_size = atoi(NEXTARG); } else if (strcmp(option, "-help") == 0 || strcmp(option, "-h") == 0) { opts->mode = Settings::HELP; return false; @@ -188,10 +194,23 @@ bool Settings::parseArgs(int argc, char **argv, Settings::WorldOptions *opts) { return false; } - if (opts->tile_size < 16) { - logger::error("Cannot render tiles this small\n"); + if (opts->fragment_size < 16) { + logger::error("Cannot render map fragments this small\n"); return false; } + + if (opts->tile_size) { + if (opts->padding != PADDING_DEFAULT && opts->padding) { + logger::error("Cannot pad tiled output !\n"); + return false; + } + + if (opts->outFile == OUTPUT_DEFAULT) { + opts->outFile = OUTPUT_TILED_DEFAULT; + + fs::create_directory(opts->outFile); + } + } } return true; diff --git a/src/settings.h b/src/settings.h index 1328366c..ac7b81e1 100644 --- a/src/settings.h +++ b/src/settings.h @@ -12,6 +12,10 @@ #define UNDEFINED 0x7FFFFFFF namespace Settings { +const string OUTPUT_DEFAULT = "output.png"; +const string OUTPUT_TILED_DEFAULT = "output"; +const size_t PADDING_DEFAULT = 5; +const size_t TILE_SIZE_DEFAULT = 0; enum Action { RENDER, DUMPCOLORS, HELP }; @@ -28,19 +32,20 @@ struct WorldOptions { World::Coordinates boundaries; // Image settings - uint16_t padding; // Should be enough + uint16_t padding; bool hideWater, hideBeacons, shading, lighting; + size_t tile_size; // 0 means no tiling // Marker storage uint8_t totalMarkers; std::array markers; - // Memory limits, legacy code for image splitting + // Memory limits size_t mem_limit; - size_t tile_size; + size_t fragment_size; WorldOptions() - : mode(RENDER), outFile("output.png"), colorFile(""), save(), + : mode(RENDER), outFile(OUTPUT_DEFAULT), colorFile(""), save(), dim("overworld") { boundaries.setUndefined(); @@ -48,12 +53,15 @@ struct WorldOptions { boundaries.maxY = mcmap::constants::max_y; hideWater = hideBeacons = shading = lighting = false; - padding = 5; + padding = PADDING_DEFAULT; + tile_size = TILE_SIZE_DEFAULT; totalMarkers = 0; + // Default 3.5G of memory maximum mem_limit = 3500 * uint64_t(1024 * 1024); - tile_size = 1024; + // Render whole regions at once + fragment_size = 1024; } fs::path regionDir() const;