albedo/albedo.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "terraingen/albedo/albedo.h" | ||
| 2 | |||
| 3 | #include "terraingen/util/checks.h" | ||
| 4 | #include "terraingen/util/log.h" | ||
| 5 | #include "terraingen/util/parallel.h" | ||
| 6 | |||
| 7 | #include <algorithm> | ||
| 8 | #include <cmath> | ||
| 9 | #include <limits> | ||
| 10 | #include <stdexcept> | ||
| 11 | |||
| 12 | namespace terraingen { | ||
| 13 | |||
| 14 | 1450427 | void ValidateAlbedoContext(const AlbedoContext& context) { | |
| 15 | 1450427 | RequirePositive(context.spacing, "Albedo sample spacing"); | |
| 16 | 1450425 | } | |
| 17 | |||
| 18 | namespace { | ||
| 19 | |||
| 20 | /// Visits the cells of one row of `bands`. | ||
| 21 | 24780 | void VisitRow(std::vector<Raster<float>>& bands, std::size_t row, const AlbedoCellVisitor& visit) { | |
| 22 | 24780 | const Raster<float>& first = bands.front(); | |
| 23 | 17127996 | for (std::size_t col = 0; col < first.Width(); ++col) { | |
| 24 | 17103216 | AlbedoSample value{}; | |
| 25 | 35013202 | for (std::size_t band = 0; band < bands.size(); ++band) { | |
| 26 | 17909986 | value[band] = bands[band](col, row); | |
| 27 | } | ||
| 28 | 17103216 | const Vec2d centre = first.CellCenter(col, row); | |
| 29 | 17103216 | visit(value, centre.x, centre.y); | |
| 30 | 35013202 | for (std::size_t band = 0; band < bands.size(); ++band) { | |
| 31 | 17909986 | bands[band](col, row) = value[band]; | |
| 32 | } | ||
| 33 | } | ||
| 34 | 24780 | } | |
| 35 | |||
| 36 | } // namespace | ||
| 37 | |||
| 38 | 82 | void ForEachAlbedoCell(std::vector<Raster<float>>& bands, unsigned threads, | |
| 39 | const AlbedoCellVisitor& visit) { | ||
| 40 | 82 | if (bands.empty()) { | |
| 41 | 1 | return; | |
| 42 | } | ||
| 43 | 81 | ParallelFor(bands.front().Height(), threads, | |
| 44 | 15353 | [&](std::size_t row) { VisitRow(bands, row, visit); }); | |
| 45 | } | ||
| 46 | |||
| 47 | 43 | void ForEachAlbedoBlock( | |
| 48 | std::vector<Raster<float>>& bands, unsigned threads, std::size_t blockRows, | ||
| 49 | const std::function<AlbedoCellVisitor(std::size_t rowBegin, std::size_t rowEnd)>& visitorFor) { | ||
| 50 | 43 | if (bands.empty()) { | |
| 51 | 1 | return; | |
| 52 | } | ||
| 53 | 42 | const std::size_t height = bands.front().Height(); | |
| 54 | 42 | const std::size_t blocks = (height + blockRows - 1) / blockRows; | |
| 55 | 42 | ParallelFor(blocks, threads, [&](std::size_t block) { | |
| 56 | 325 | const std::size_t rowBegin = block * blockRows; | |
| 57 | 325 | const std::size_t rowEnd = std::min(height, rowBegin + blockRows); | |
| 58 | 325 | const AlbedoCellVisitor visit = visitorFor(rowBegin, rowEnd); | |
| 59 | 9914 | for (std::size_t row = rowBegin; row < rowEnd; ++row) { | |
| 60 | 9589 | VisitRow(bands, row, visit); | |
| 61 | } | ||
| 62 | 325 | }); | |
| 63 | } | ||
| 64 | |||
| 65 | 74 | void AlbedoSource::Fill(std::vector<Raster<float>>& bands, const AlbedoContext& context) const { | |
| 66 | 74 | ForEachAlbedoCell(bands, context.threads, [&](AlbedoSample& value, double x, double y) { | |
| 67 | 8529267 | value = ValueAt(x, y, context); | |
| 68 | 8529267 | }); | |
| 69 | 74 | } | |
| 70 | |||
| 71 | 7 | void AlbedoLayer::Apply(std::vector<Raster<float>>& bands, const AlbedoContext& context) const { | |
| 72 | 7 | const std::size_t count = bands.size(); | |
| 73 | 7 | ForEachAlbedoCell(bands, context.threads, [&](AlbedoSample& value, double x, double y) { | |
| 74 | 1514274 | ApplyAt(value, count, x, y, context); | |
| 75 | 1514274 | }); | |
| 76 | 7 | } | |
| 77 | |||
| 78 | namespace { | ||
| 79 | |||
| 80 | 1724 | std::size_t CheckAlbedo(const Albedo& albedo, const AlbedoContext& context) { | |
| 81 | 1724 | if (!albedo.source) { | |
| 82 | 1 | throw std::invalid_argument("An albedo needs a source"); | |
| 83 | } | ||
| 84 | 3421 | for (const auto& layer : albedo.layers) { | |
| 85 | 1699 | if (!layer) { | |
| 86 | 1 | throw std::invalid_argument("An albedo layer is null"); | |
| 87 | } | ||
| 88 | } | ||
| 89 | 1722 | const std::size_t bands = albedo.source->Bands(); | |
| 90 | 1722 | if (bands != 1 && bands != kMaxAlbedoBands) { | |
| 91 | 1 | throw std::invalid_argument("An albedo source must have 1 or 3 bands"); | |
| 92 | } | ||
| 93 | 1721 | ValidateAlbedoContext(context); | |
| 94 | 1720 | return bands; | |
| 95 | } | ||
| 96 | |||
| 97 | } // namespace | ||
| 98 | |||
| 99 | 79 | std::vector<Raster<float>> EvaluateAlbedo(const Albedo& albedo, const RasterGrid& grid, | |
| 100 | AlbedoContext context) { | ||
| 101 | 79 | if (grid.width == 0 || grid.height == 0) { | |
| 102 | 1 | throw std::invalid_argument("Cannot evaluate an albedo over an empty grid"); | |
| 103 | } | ||
| 104 | 78 | RequireNonNegative(context.spacing, "Albedo sample spacing"); | |
| 105 | 77 | context.spacing = std::fmax(context.spacing, grid.Spacing(albedo.surface)); | |
| 106 | 77 | const std::size_t count = CheckAlbedo(albedo, context); | |
| 107 | 74 | std::vector<Raster<float>> bands; | |
| 108 | 178 | for (std::size_t band = 0; band < count; ++band) { | |
| 109 | 104 | Raster<float> raster = grid.MakeRaster(0.0f); | |
| 110 | 104 | raster.SetNoData(std::numeric_limits<float>::quiet_NaN()); | |
| 111 | 104 | bands.push_back(std::move(raster)); | |
| 112 | 104 | } | |
| 113 | 74 | albedo.source->Fill(bands, context); | |
| 114 | 130 | for (std::size_t n = 0; n < albedo.layers.size(); ++n) { | |
| 115 | 56 | const Stopwatch stopwatch; | |
| 116 | 56 | albedo.layers[n]->Apply(bands, context); | |
| 117 | 56 | LogDebug("albedo", "Applied albedo layer ", n + 1, " in ", | |
| 118 | 112 | FormatDuration(stopwatch.Milliseconds())); | |
| 119 | } | ||
| 120 | 74 | return bands; | |
| 121 | } | ||
| 122 | |||
| 123 | 1647 | AlbedoSample EvaluateAlbedoAt(const Albedo& albedo, double x, double y, | |
| 124 | const AlbedoContext& context) { | ||
| 125 | 1647 | const std::size_t count = CheckAlbedo(albedo, context); | |
| 126 | 1646 | AlbedoSample value = albedo.source->ValueAt(x, y, context); | |
| 127 | 4918 | for (std::size_t band = count; band < kMaxAlbedoBands; ++band) { | |
| 128 | 3272 | value[band] = 0.0f; | |
| 129 | } | ||
| 130 | 3288 | for (const auto& layer : albedo.layers) { | |
| 131 | 1642 | layer->ApplyAt(value, count, x, y, context); | |
| 132 | } | ||
| 133 | 1646 | return value; | |
| 134 | } | ||
| 135 | |||
| 136 | } // namespace terraingen | ||
| 137 |