| 13 | namespace OpenLoco::World::MapGenerator |
| 14 | { |
| 15 | void PngTerrainGenerator::generate(const Scenario::Options& options, const fs::path& path, HeightMap& heightMap) |
| 16 | { |
| 17 | if (!fs::is_regular_file(path)) |
| 18 | { |
| 19 | Logging::error("Can't find heightmap file ({})", path); |
| 20 | return; |
| 21 | } |
| 22 | |
| 23 | auto pngImage = Gfx::PngImage::loadFromFile(path); |
| 24 | if (pngImage == nullptr) |
| 25 | { |
| 26 | Logging::error("Can't load heightmap file ({})", path); |
| 27 | return; |
| 28 | } |
| 29 | |
| 30 | // TODO: Move the constants to a more sensible place, values are taken from TileManager::adjustSurfaceHeight |
| 31 | constexpr int minLandHeight = 4 / kMicroToSmallZStep; |
| 32 | constexpr int maxLandHeight = 160 / kMicroToSmallZStep; |
| 33 | |
| 34 | const int heightRange = maxLandHeight - std::max<int>(minLandHeight, options.minLandHeight); |
| 35 | |
| 36 | std::fill_n(heightMap.data(), heightMap.size(), options.minLandHeight); |
| 37 | |
| 38 | // Map the entire map area to the image with interpolation |
| 39 | for (int32_t y = 0; y < World::kMapRows; y++) |
| 40 | { |
| 41 | for (int32_t x = 0; x < World::kMapColumns; x++) |
| 42 | { |
| 43 | // Map from map coordinates to image coordinates |
| 44 | const float imgX = (x * pngImage->width) / static_cast<float>(World::kMapColumns); |
| 45 | const float imgY = (y * pngImage->height) / static_cast<float>(World::kMapRows); |
| 46 | |
| 47 | // Bilinear interpolation coordinates |
| 48 | const int x0 = static_cast<int>(imgX); |
| 49 | const int y0 = static_cast<int>(imgY); |
| 50 | const int x1 = std::min(x0 + 1, pngImage->width - 1); |
| 51 | const int y1 = std::min(y0 + 1, pngImage->height - 1); |
| 52 | |
| 53 | const float fx = imgX - x0; |
| 54 | const float fy = imgY - y0; |
| 55 | |
| 56 | // Sample the four corners for bilinear interpolation |
| 57 | const auto c00 = pngImage->getPixel(x0, y0); |
| 58 | const auto c10 = pngImage->getPixel(x1, y0); |
| 59 | const auto c01 = pngImage->getPixel(x0, y1); |
| 60 | const auto c11 = pngImage->getPixel(x1, y1); |
| 61 | |
| 62 | // Get max of RGB channels for each corner |
| 63 | const auto h00 = std::max({ c00.r, c00.g, c00.b }); |
| 64 | const auto h10 = std::max({ c10.r, c10.g, c10.b }); |
| 65 | const auto h01 = std::max({ c01.r, c01.g, c01.b }); |
| 66 | const auto h11 = std::max({ c11.r, c11.g, c11.b }); |
| 67 | |
| 68 | // Perform bilinear interpolation |
| 69 | const auto h0 = h00 * (1.0f - fx) + h10 * fx; |
| 70 | const auto h1 = h01 * (1.0f - fx) + h11 * fx; |
| 71 | const auto imgHeight = h0 * (1.0f - fy) + h1 * fy; |
| 72 |
no test coverage detected