Skip to content

Commit 3f2c3fe

Browse files
committed
Add StrataCells node
1 parent 0cee97c commit 3f2c3fe

File tree

5 files changed

+213
-1
lines changed

5 files changed

+213
-1
lines changed

Hesiod/include/hesiod/model/nodes/node_factory.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ DECLARE_NODE(snow_simulation)
273273
DECLARE_NODE(stamping)
274274
DECLARE_NODE(std_local)
275275
DECLARE_NODE(strata)
276+
DECLARE_NODE(strata_cells)
276277
DECLARE_NODE(steepen_convective)
277278
DECLARE_NODE(step)
278279
DECLARE_NODE(terrace)

Hesiod/src/gui/widgets/node_widgets/node_widget_factory.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ QWidget *node_widget_factory(const std::string &node_type,
2828
"Recurve",
2929
"Rifts",
3030
"ShatteredPeak",
31+
"StrataCells",
3132
"WaterDepthFromMask",
3233
"WatershedRidge"};
3334
std::vector<std::string> deprecated_nodes = {"TextureAdvectionWarp"};

Hesiod/src/model/nodes/node_factory.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ std::map<std::string, std::string> get_node_inventory()
380380
{"SteepenConvective", "Filter/Recast"},
381381
{"Step", "Primitive/Function"},
382382
{"Strata", "Erosion/Stratify"},
383+
{"StrataCells", "Erosion/Stratify"},
383384
{"Terrace", "Filter/Recurve"},
384385
{"TextureAdvectionParticle", "Texture"},
385386
{"TextureAdvectionWarp", "WIP/DEPRECATED"},
@@ -697,6 +698,7 @@ std::shared_ptr<gnode::Node> node_factory(const std::string &node_type,
697698
SETUP_NODE(SteepenConvective, steepen_convective);
698699
SETUP_NODE(Step, step);
699700
SETUP_NODE(Strata, strata);
701+
SETUP_NODE(StrataCells, strata_cells);
700702
SETUP_NODE(Terrace, terrace);
701703
SETUP_NODE(TextureAdvectionParticle, texture_advection_particle);
702704
SETUP_NODE(TextureAdvectionWarp, texture_advection_warp);
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
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, &params](std::vector<const hmap::Array *> p_arrays_in,
173+
std::vector<hmap::Array *> p_arrays_out,
174+
const hmap::TileRegion &region)
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

Comments
 (0)