layers/craters/shaping/crater_kernel.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "terraingen/layers/craters/shaping/crater_kernel.h" | ||
| 2 | |||
| 3 | #include "terraingen/layers/craters/degradation/diffusion.h" | ||
| 4 | #include "terraingen/layers/craters/profiles/complex.h" | ||
| 5 | #include "terraingen/layers/craters/profiles/fassett_thomson.h" | ||
| 6 | #include "terraingen/layers/craters/profiles/martin_parkes_dunstan.h" | ||
| 7 | #include "terraingen/layers/craters/shaping/apply.h" | ||
| 8 | #include "terraingen/noise/hash.h" | ||
| 9 | |||
| 10 | #include <map> | ||
| 11 | #include <stdexcept> | ||
| 12 | |||
| 13 | namespace terraingen { | ||
| 14 | |||
| 15 | namespace { | ||
| 16 | |||
| 17 | /// The key that seeds `crater`'s noise or texture with `seed`. | ||
| 18 | 4248544 | std::uint32_t CraterKey(const Crater& crater, std::uint32_t seed) { | |
| 19 | 4248544 | return PcgHash(static_cast<std::uint32_t>(crater.id) ^ | |
| 20 | 8497088 | PcgHash(static_cast<std::uint32_t>(crater.id >> 32) ^ seed)); | |
| 21 | } | ||
| 22 | |||
| 23 | /// Whether the simple profile shapes `crater` in `state`: not where the table or the complex | ||
| 24 | /// form does. | ||
| 25 | 1775188 | bool ShapesSimply(const CraterShapeKernel& kernel, double diameter, const CraterState& state) { | |
| 26 | 1775188 | const CraterProfileKernel& p = kernel.profile; | |
| 27 | 1775188 | if (p.diffused == 1u && state.diffusion > 0.0) { | |
| 28 | 1601621 | return false; | |
| 29 | } | ||
| 30 | 173567 | return !(p.complex == 1u && diameter >= static_cast<double>(p.transitionDiameter)); | |
| 31 | } | ||
| 32 | |||
| 33 | } // namespace | ||
| 34 | |||
| 35 | 1499330 | std::optional<CraterShapeKernel> MakeCraterShapeKernel(const CraterProfile& profile, | |
| 36 | const CraterApplyOptions& options) { | ||
| 37 | 1499330 | if (options.surface.IsBody()) { | |
| 38 | 482291 | return std::nullopt; | |
| 39 | } | ||
| 40 | 1017039 | CraterShapeKernel k; | |
| 41 | 1017039 | const CraterProfile* fresh = &profile; | |
| 42 | 1017039 | if (const auto* diffused = dynamic_cast<const DiffusedProfile*>(&profile)) { | |
| 43 | 103719 | k.profile.diffused = 1u; | |
| 44 | 103719 | k.profile.diffusedExtent = static_cast<float>(0.5 * diffused->ReachRadii()); | |
| 45 | 103719 | k.profile.diffusedCells = static_cast<std::uint32_t>(diffused->Cells()); | |
| 46 | 103719 | k.profile.agesOffset = 0u; | |
| 47 | 103719 | k.profile.ageCount = static_cast<std::uint32_t>(diffused->Ages().size()); | |
| 48 | 103719 | fresh = &diffused->Fresh(); | |
| 49 | } | ||
| 50 | 1017039 | if (const auto* complex = dynamic_cast<const ComplexCraterProfile*>(fresh)) { | |
| 51 | 100803 | const ComplexCraterShape& shape = complex->Shape(); | |
| 52 | 100803 | const KilometrePowerLaw width = shape.peakDiameter ? *shape.peakDiameter : shape.peakVolume; | |
| 53 | 100803 | k.profile.complex = 1u; | |
| 54 | 100803 | k.profile.transitionDiameter = static_cast<float>(shape.transitionDiameter); | |
| 55 | 100803 | k.profile.floorCoefficient = static_cast<float>(shape.floorDiameter.coefficient); | |
| 56 | 100803 | k.profile.floorExponent = static_cast<float>(shape.floorDiameter.exponent); | |
| 57 | 100803 | k.profile.peakHeightCoefficient = static_cast<float>(shape.peakHeight.coefficient); | |
| 58 | 100803 | k.profile.peakHeightExponent = static_cast<float>(shape.peakHeight.exponent); | |
| 59 | 100803 | k.profile.peakWidthCoefficient = static_cast<float>(width.coefficient); | |
| 60 | 100803 | k.profile.peakWidthExponent = static_cast<float>(width.exponent); | |
| 61 | 100803 | k.profile.peakFromVolume = shape.peakDiameter ? 0u : 1u; | |
| 62 | 100803 | k.profile.peakFitMaxDiameter = static_cast<float>(shape.peakFitMaxDiameter); | |
| 63 | 100803 | k.profile.ejectaExponent = static_cast<float>(shape.ejectaExponent); | |
| 64 | 100803 | k.profile.complexReachRadii = static_cast<float>(shape.reachRadii); | |
| 65 | 100803 | fresh = &complex->Simple(); | |
| 66 | } | ||
| 67 | 1017039 | if (const auto* ft = dynamic_cast<const FassettThomsonProfile*>(fresh)) { | |
| 68 | 294013 | k.profile.simple = CraterProfileKind::FassettThomson; | |
| 69 | 294013 | k.profile.rim = static_cast<std::uint32_t>(ft->Rim()); | |
| 70 | 723026 | } else if (dynamic_cast<const MartinParkesDunstanProfile*>(fresh) != nullptr) { | |
| 71 | 152232 | k.profile.simple = CraterProfileKind::MartinParkesDunstan; | |
| 72 | } else { | ||
| 73 | 570794 | return std::nullopt; | |
| 74 | } | ||
| 75 | 446245 | k.reachRadii = static_cast<float>(CraterReachRadii(profile, options)); | |
| 76 | 446245 | k.profileReachRadii = static_cast<float>(profile.ReachRadii()); | |
| 77 | 446245 | k.blendStart = static_cast<float>(options.blend.startRadii); | |
| 78 | 446245 | k.blendEnd = static_cast<float>(options.blend.endRadii); | |
| 79 | 446245 | k.blendShape = static_cast<std::uint32_t>(options.blend.shape); | |
| 80 | 446245 | k.interiorReplacement = static_cast<float>(options.interiorReplacement); | |
| 81 | 446245 | k.mantling = static_cast<float>(options.texture.mantling); | |
| 82 | 446245 | k.ejectaReachRadii = static_cast<float>(options.texture.ejectaReachRadii); | |
| 83 | 446245 | const RimSmoothing& rim = options.rimSmoothing; | |
| 84 | 446245 | k.rimSigma = static_cast<float>(rim.sigmaRadii); | |
| 85 | 446245 | k.rimInner = static_cast<float>(rim.innerRadii); | |
| 86 | 446245 | k.rimOuter = static_cast<float>(rim.outerRadii); | |
| 87 | 446245 | k.rimStrength = static_cast<float>(rim.strength); | |
| 88 | 446245 | k.rimShape = static_cast<std::uint32_t>(rim.shape); | |
| 89 | 446245 | const CraterNoise& noise = options.noise; | |
| 90 | 446245 | k.noiseAmplitude = static_cast<float>(noise.amplitude); | |
| 91 | 446245 | k.noiseWavelength = static_cast<float>(noise.wavelengthRadii); | |
| 92 | 446245 | k.noiseOctaves = noise.octaves; | |
| 93 | 446245 | k.noiseGain = static_cast<float>(noise.gain); | |
| 94 | 446245 | k.noiseRimAmplitude = static_cast<float>(noise.rimAmplitude); | |
| 95 | 446245 | k.noiseRimWavelength = static_cast<float>(noise.rimWavelengthRadii); | |
| 96 | 446245 | k.noiseRimInner = static_cast<float>(noise.rimInnerRadii); | |
| 97 | 446245 | k.noiseRimOuter = static_cast<float>(noise.rimOuterRadii); | |
| 98 | 446245 | const CraterTexture& texture = options.texture; | |
| 99 | 446245 | k.wallStreaks = static_cast<float>(texture.wallStreaks); | |
| 100 | 446245 | k.wallStreakWidth = static_cast<float>(texture.wallStreakWidthRadii); | |
| 101 | 446245 | k.ejecta = static_cast<float>(texture.ejecta); | |
| 102 | 446245 | k.ejectaLineation = static_cast<float>(texture.ejectaLineationRadii); | |
| 103 | 446245 | k.minFreshness = static_cast<float>(texture.minFreshness); | |
| 104 | 446245 | return k; | |
| 105 | } | ||
| 106 | |||
| 107 | 448966 | CraterKernelBatch MakeCraterKernelBatch(const CraterShapeKernel& kernel, | |
| 108 | const CraterProfile& profile, | ||
| 109 | const std::vector<PreparedCrater>& prepared, | ||
| 110 | const std::vector<std::size_t>& craters, | ||
| 111 | const CraterApplyOptions& options) { | ||
| 112 | 448966 | CraterKernelBatch batch; | |
| 113 | 448966 | const auto* diffused = dynamic_cast<const DiffusedProfile*>(&profile); | |
| 114 | 448966 | if (kernel.profile.diffused == 1u && diffused != nullptr) { | |
| 115 | 17175888 | for (const double age : diffused->Ages()) { | |
| 116 | 17069864 | batch.data.push_back(static_cast<float>(age)); | |
| 117 | } | ||
| 118 | } | ||
| 119 | 448966 | std::map<const std::vector<float>*, std::uint32_t> tables; | |
| 120 | 448966 | batch.records.reserve(craters.size()); | |
| 121 | 2573238 | for (const std::size_t k : craters) { | |
| 122 | 2124273 | const PreparedCrater& p = prepared[k]; | |
| 123 | 2124273 | const CraterState& state = p.state; | |
| 124 | 2124273 | const double diameter = p.crater.diameter; | |
| 125 | 3899461 | if (kernel.profile.simple == CraterProfileKind::MartinParkesDunstan && | |
| 126 | 1775188 | ShapesSimply(kernel, diameter, state)) { | |
| 127 | 173567 | const CraterMorphometry& shaped = | |
| 128 | 173567 | kernel.profile.diffused == 1u ? state.fresh : state.current; | |
| 129 | 173567 | if (!(shaped.depthRatio > 0.0 && shaped.rimHeightRatio >= 0.0 && | |
| 130 | 173567 | shaped.rimHeightRatio <= state.fresh.rimHeightRatio)) { | |
| 131 | throw std::invalid_argument("Crater state needs a positive depth and 0 <= rim " | ||
| 132 | 1 | "height <= fresh rim height"); | |
| 133 | } | ||
| 134 | } | ||
| 135 | 2124272 | CraterRecord r; | |
| 136 | 2124272 | r.diameter = static_cast<float>(diameter); | |
| 137 | 2124272 | r.resolution = static_cast<float>(p.resolution); | |
| 138 | 2124272 | r.freshDepth = static_cast<float>(state.fresh.depthRatio); | |
| 139 | 2124272 | r.freshRim = static_cast<float>(state.fresh.rimHeightRatio); | |
| 140 | 2124272 | r.currentDepth = static_cast<float>(state.current.depthRatio); | |
| 141 | 2124272 | r.currentRim = static_cast<float>(state.current.rimHeightRatio); | |
| 142 | 2124272 | r.diffusion = static_cast<float>(state.diffusion); | |
| 143 | 2124272 | const CraterOutline& outline = p.outline; | |
| 144 | 2124272 | r.ellipticity = static_cast<float>(outline.ellipticity); | |
| 145 | 2124272 | r.heading = static_cast<float>(outline.heading); | |
| 146 | 2124272 | r.firstHarmonic = static_cast<std::uint32_t>(outline.firstHarmonic); | |
| 147 | 2124272 | r.harmonicCount = static_cast<std::uint32_t>(outline.amplitudes.size()); | |
| 148 | 2124272 | r.harmonicOffset = static_cast<std::uint32_t>(batch.data.size()); | |
| 149 | 2341280 | for (std::size_t n = 0; n < outline.amplitudes.size(); ++n) { | |
| 150 | 217008 | batch.data.push_back(static_cast<float>(outline.amplitudes[n])); | |
| 151 | 217008 | batch.data.push_back(static_cast<float>(outline.phases[n])); | |
| 152 | } | ||
| 153 | 2124272 | r.tableCount = static_cast<std::uint32_t>(outline.table.size()); | |
| 154 | 2124272 | r.tableOffset = static_cast<std::uint32_t>(batch.data.size()); | |
| 155 | 4172272 | for (const double deviation : outline.table) { | |
| 156 | 2048000 | batch.data.push_back(static_cast<float>(deviation)); | |
| 157 | } | ||
| 158 | 2124272 | r.markCount = static_cast<std::uint32_t>(outline.marks.size()); | |
| 159 | 2124272 | r.markOffset = static_cast<std::uint32_t>(batch.data.size()); | |
| 160 | 2124272 | if (!outline.marks.empty()) { | |
| 161 | 4709 | for (const double radii : | |
| 162 | 23545 | {outline.marksInnerRadii, outline.marksOuterRadii, outline.marksFadeRadii}) { | |
| 163 | 14127 | batch.data.push_back(static_cast<float>(radii)); | |
| 164 | } | ||
| 165 | 216614 | for (const double knot : outline.marks) { | |
| 166 | 211905 | batch.data.push_back(static_cast<float>(knot)); | |
| 167 | } | ||
| 168 | } | ||
| 169 | 2124272 | if (kernel.profile.diffused == 1u && diffused != nullptr && state.diffusion > 0.0) { | |
| 170 | const std::shared_ptr<const std::vector<float>> table = | ||
| 171 | 1652167 | diffused->HeightTable(state.fresh, diameter); | |
| 172 | 1652167 | const auto [at, added] = | |
| 173 | 1652167 | tables.try_emplace(table.get(), static_cast<std::uint32_t>(batch.data.size())); | |
| 174 | 1652167 | if (added) { | |
| 175 | 85822 | batch.data.insert(batch.data.end(), table->begin(), table->end()); | |
| 176 | } | ||
| 177 | 1652167 | r.diffusionOffset = at->second; | |
| 178 | 1652167 | } | |
| 179 | 2124272 | if (p.datum) { | |
| 180 | 487853 | r.hasDatum = 1u; | |
| 181 | 487853 | r.datumA = static_cast<float>(p.datum->a); | |
| 182 | 487853 | r.datumB = static_cast<float>(p.datum->b); | |
| 183 | 487853 | r.datumC = static_cast<float>(p.datum->c); | |
| 184 | } | ||
| 185 | 2124272 | r.shiftX = static_cast<float>(p.slope.shift.x); | |
| 186 | 2124272 | r.shiftY = static_cast<float>(p.slope.shift.y); | |
| 187 | 2124272 | r.upslopeX = static_cast<float>(p.slope.upslope.x); | |
| 188 | 2124272 | r.upslopeY = static_cast<float>(p.slope.upslope.y); | |
| 189 | 2124272 | r.noiseKey = CraterKey(p.crater, options.noise.seed); | |
| 190 | 2124272 | r.textureKey = CraterKey(p.crater, options.texture.seed ^ 0x54455854u); | |
| 191 | 2124272 | batch.records.push_back(r); | |
| 192 | } | ||
| 193 | 897930 | return batch; | |
| 194 | 448967 | } | |
| 195 | |||
| 196 | } // namespace terraingen | ||
| 197 |