|
| 1 | +/* Copyright (c) 2026 Otto Link. Distributed under the terms of the GNU General |
| 2 | + * Public License. The full license is in the file LICENSE, distributed with |
| 3 | + * this software. */ |
| 4 | +#include "highmap/erosion.hpp" |
| 5 | +#include "highmap/opencl/gpu_opencl.hpp" |
| 6 | +#include "highmap/primitives.hpp" |
| 7 | + |
| 8 | +#include "attributes.hpp" |
| 9 | + |
| 10 | +#include "hesiod/logger.hpp" |
| 11 | +#include "hesiod/model/nodes/base_node.hpp" |
| 12 | +#include "hesiod/model/nodes/post_process.hpp" |
| 13 | + |
| 14 | +using namespace attr; |
| 15 | + |
| 16 | +namespace hesiod |
| 17 | +{ |
| 18 | + |
| 19 | +// ----------------------------------------------------------------------------- |
| 20 | +// Ports & Attributes |
| 21 | +// ----------------------------------------------------------------------------- |
| 22 | + |
| 23 | +constexpr const char *P_INPUT = "input"; |
| 24 | +constexpr const char *P_MASK = "mask"; |
| 25 | +constexpr const char *P_NOISE_X = "noise_x"; |
| 26 | +constexpr const char *P_NOISE_Y = "noise_y"; |
| 27 | +constexpr const char *P_OUTPUT = "output"; |
| 28 | + |
| 29 | +constexpr const char *A_KW = "kw"; |
| 30 | +constexpr const char *A_AMP = "amp"; |
| 31 | +constexpr const char *A_SEED = "seed"; |
| 32 | +constexpr const char *A_GAMMA = "gamma"; |
| 33 | +constexpr const char *A_GAMMA_LATERAL = "gamma_lateral"; |
| 34 | +constexpr const char *A_ANGLE = "angle"; |
| 35 | +constexpr const char *A_ENABLE_DEFAULT_NOISE = "enable_default_noise"; |
| 36 | +constexpr const char *A_NOISE_AMP = "noise_amp"; |
| 37 | +constexpr const char *A_ABSOLUTE_DISPLACEMENT = "absolute_displacement"; |
| 38 | +constexpr const char *A_OCCURENCE_PROBABILITY = "occurence_probability"; |
| 39 | +constexpr const char *A_OCTAVES = "octaves"; |
| 40 | +constexpr const char *A_PERSISTENCE = "persistence"; |
| 41 | +constexpr const char *A_LACUNARITY = "lacunarity"; |
| 42 | + |
| 43 | +// ----------------------------------------------------------------------------- |
| 44 | +// Setup |
| 45 | +// ----------------------------------------------------------------------------- |
| 46 | + |
| 47 | +void setup_strata_cells_node(BaseNode &node) |
| 48 | +{ |
| 49 | + Logger::log()->trace("setup node {}", node.get_label()); |
| 50 | + |
| 51 | + // port(s) |
| 52 | + node.add_port<hmap::VirtualArray>(gnode::PortType::IN, P_INPUT); |
| 53 | + node.add_port<hmap::VirtualArray>(gnode::PortType::IN, P_MASK); |
| 54 | + node.add_port<hmap::VirtualArray>(gnode::PortType::IN, P_NOISE_X); |
| 55 | + node.add_port<hmap::VirtualArray>(gnode::PortType::IN, P_NOISE_Y); |
| 56 | + node.add_port<hmap::VirtualArray>(gnode::PortType::OUT, P_OUTPUT, CONFIG(node)); |
| 57 | + |
| 58 | + // attribute(s) |
| 59 | + // clang-format off |
| 60 | + node.add_attr<WaveNbAttribute>(A_KW, "Spatial Frequency", glm::vec2(2.f, 6.f), 0.f, FLT_MAX, false); |
| 61 | + node.add_attr<FloatAttribute>(A_AMP, "Strata Strength", 0.2f, 0.f, 1.f); |
| 62 | + node.add_attr<SeedAttribute>(A_SEED, "Seed"); |
| 63 | + node.add_attr<FloatAttribute>(A_GAMMA, "Longitudinal Sharpness", 0.5f, 0.01f, 2.f); |
| 64 | + node.add_attr<FloatAttribute>(A_GAMMA_LATERAL, "Lateral Sharpness", 0.4f, 0.01f, 2.f); |
| 65 | + node.add_attr<FloatAttribute>(A_ANGLE, "Orientation Angle", 0.f, -180.f, 180.f, "{:.0f}°"); |
| 66 | + node.add_attr<BoolAttribute>(A_ENABLE_DEFAULT_NOISE, "Enable Base Noise", true); |
| 67 | + node.add_attr<FloatAttribute>(A_NOISE_AMP, "Base Noise Amplitude", 0.05f, 0.f, 1.f); |
| 68 | + node.add_attr<BoolAttribute>(A_ABSOLUTE_DISPLACEMENT, "Use Absolute Displacement", false); |
| 69 | + node.add_attr<FloatAttribute>(A_OCCURENCE_PROBABILITY, "Feature Occurrence Probability", 1.f, 0.f, 1.f); |
| 70 | + node.add_attr<IntAttribute>(A_OCTAVES, "Octaves", 6, 1, 32); |
| 71 | + node.add_attr<FloatAttribute>(A_PERSISTENCE, "Persistence", 0.5f, 0.f, 1.f); |
| 72 | + node.add_attr<FloatAttribute>(A_LACUNARITY, "Lacunarity", 2.2f, 0.01f, 4.f); |
| 73 | + // clang-format on |
| 74 | + |
| 75 | + // attribute(s) order |
| 76 | + node.set_attr_ordered_key({"_GROUPBOX_BEGIN_Wave Definition", |
| 77 | + A_KW, |
| 78 | + A_AMP, |
| 79 | + A_SEED, |
| 80 | + "_GROUPBOX_END_", |
| 81 | + // |
| 82 | + "_GROUPBOX_BEGIN_Shape Control", |
| 83 | + A_GAMMA, |
| 84 | + A_GAMMA_LATERAL, |
| 85 | + A_ANGLE, |
| 86 | + A_ABSOLUTE_DISPLACEMENT, |
| 87 | + A_OCCURENCE_PROBABILITY, |
| 88 | + "_GROUPBOX_END_", |
| 89 | + // |
| 90 | + "_GROUPBOX_BEGIN_Erosion Layers", |
| 91 | + A_OCTAVES, |
| 92 | + A_PERSISTENCE, |
| 93 | + A_LACUNARITY, |
| 94 | + "_GROUPBOX_END_", |
| 95 | + // |
| 96 | + "_GROUPBOX_BEGIN_Noise Modulation", |
| 97 | + A_ENABLE_DEFAULT_NOISE, |
| 98 | + A_NOISE_AMP, |
| 99 | + "_GROUPBOX_END_"}); |
| 100 | + |
| 101 | + setup_pre_process_mask_attributes(node); |
| 102 | + setup_post_process_heightmap_attributes(node, |
| 103 | + {.add_mix = true, .remap_active_state = false}); |
| 104 | +} |
| 105 | + |
| 106 | +// ----------------------------------------------------------------------------- |
| 107 | +// Compute |
| 108 | +// ----------------------------------------------------------------------------- |
| 109 | + |
| 110 | +void compute_strata_cells_node(BaseNode &node) |
| 111 | +{ |
| 112 | + Logger::log()->trace("computing node [{}]/[{}]", node.get_label(), node.get_id()); |
| 113 | + |
| 114 | + auto *p_in = node.get_value_ref<hmap::VirtualArray>(P_INPUT); |
| 115 | + auto *p_dx = node.get_value_ref<hmap::VirtualArray>(P_NOISE_X); |
| 116 | + auto *p_dy = node.get_value_ref<hmap::VirtualArray>(P_NOISE_Y); |
| 117 | + auto *p_mask = node.get_value_ref<hmap::VirtualArray>(P_MASK); |
| 118 | + auto *p_out = node.get_value_ref<hmap::VirtualArray>(P_OUTPUT); |
| 119 | + |
| 120 | + if (!p_in) |
| 121 | + return; |
| 122 | + |
| 123 | + // --- Params wrapper |
| 124 | + |
| 125 | + const auto params = [&node]() |
| 126 | + { |
| 127 | + struct P |
| 128 | + { |
| 129 | + glm::vec2 kw; |
| 130 | + float amp; |
| 131 | + uint seed; |
| 132 | + float gamma; |
| 133 | + float gamma_lateral; |
| 134 | + float angle; |
| 135 | + bool enable_default_noise; |
| 136 | + float noise_amp; |
| 137 | + bool absolute_displacement; |
| 138 | + float occurence_probability; |
| 139 | + int octaves; |
| 140 | + float persistence; |
| 141 | + float lacunarity; |
| 142 | + }; |
| 143 | + return P{ |
| 144 | + .kw = node.get_attr<WaveNbAttribute>(A_KW), |
| 145 | + .amp = node.get_attr<FloatAttribute>(A_AMP), |
| 146 | + .seed = node.get_attr<SeedAttribute>(A_SEED), |
| 147 | + .gamma = node.get_attr<FloatAttribute>(A_GAMMA), |
| 148 | + .gamma_lateral = node.get_attr<FloatAttribute>(A_GAMMA_LATERAL), |
| 149 | + .angle = node.get_attr<FloatAttribute>(A_ANGLE), |
| 150 | + .enable_default_noise = node.get_attr<BoolAttribute>(A_ENABLE_DEFAULT_NOISE), |
| 151 | + .noise_amp = node.get_attr<FloatAttribute>(A_NOISE_AMP), |
| 152 | + .absolute_displacement = node.get_attr<BoolAttribute>(A_ABSOLUTE_DISPLACEMENT), |
| 153 | + .occurence_probability = node.get_attr<FloatAttribute>(A_OCCURENCE_PROBABILITY), |
| 154 | + .octaves = node.get_attr<IntAttribute>(A_OCTAVES), |
| 155 | + .persistence = node.get_attr<FloatAttribute>(A_PERSISTENCE), |
| 156 | + .lacunarity = node.get_attr<FloatAttribute>(A_LACUNARITY), |
| 157 | + }; |
| 158 | + }(); |
| 159 | + |
| 160 | + // --- Prepare mask |
| 161 | + |
| 162 | + std::shared_ptr<hmap::VirtualArray> sp_mask = pre_process_mask(node, p_mask, *p_in); |
| 163 | + |
| 164 | + // --- Compute |
| 165 | + |
| 166 | + float hmin = p_in->min(node.cfg().cm_cpu); |
| 167 | + float hmax = p_in->max(node.cfg().cm_cpu); |
| 168 | + |
| 169 | + hmap::for_each_tile( |
| 170 | + {p_in, p_dx, p_dy, p_mask}, |
| 171 | + {p_out}, |
| 172 | + [&node, ¶ms](std::vector<const hmap::Array *> p_arrays_in, |
| 173 | + std::vector<hmap::Array *> p_arrays_out, |
| 174 | + const hmap::TileRegion ®ion) |
| 175 | + { |
| 176 | + const auto [pa_in, pa_dx, pa_dy, pa_mask] = unpack<4>(p_arrays_in); |
| 177 | + auto [pa_out] = unpack<1>(p_arrays_out); |
| 178 | + |
| 179 | + *pa_out = *pa_in; |
| 180 | + |
| 181 | + hmap::gpu::strata_cells_fbm(*pa_out, |
| 182 | + params.kw, |
| 183 | + params.amp, |
| 184 | + params.seed, |
| 185 | + pa_mask, |
| 186 | + params.gamma, |
| 187 | + params.gamma_lateral, |
| 188 | + params.angle, |
| 189 | + params.enable_default_noise, |
| 190 | + params.noise_amp, |
| 191 | + params.absolute_displacement, |
| 192 | + params.occurence_probability, |
| 193 | + params.octaves, |
| 194 | + params.persistence, |
| 195 | + params.lacunarity, |
| 196 | + pa_dx, |
| 197 | + pa_dy, |
| 198 | + region.bbox); |
| 199 | + }, |
| 200 | + node.cfg().cm_gpu); |
| 201 | + |
| 202 | + p_out->remap(hmin, hmax, node.cfg().cm_cpu); |
| 203 | + |
| 204 | + // post-process |
| 205 | + post_process_heightmap(node, *p_out, p_in); |
| 206 | +} |
| 207 | + |
| 208 | +} // namespace hesiod |
0 commit comments