mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 02:49:19 +02:00
Editor terrain: area fill with blurred or sharp edge, texture rotation
This commit is contained in:
@@ -358,7 +358,8 @@ void terrain_streamer::update_material(editor_terrain &Chunk)
|
||||
{
|
||||
auto const layer{data.layers[slot] == heightmap::no_layer ? std::uint16_t{0} : data.layers[slot]};
|
||||
auto const size{layer < m_manifest.layers.size() ? m_manifest.layers[layer].size : 0.f};
|
||||
layers.push_back({palette_material(layer), size});
|
||||
auto const rotation{layer < m_manifest.layers.size() ? m_manifest.layers[layer].rotation : 0.f};
|
||||
layers.push_back({palette_material(layer), size, rotation});
|
||||
}
|
||||
auto const corner{data.corner()};
|
||||
auto const packcorner{heightmap::pack_corner(heightmap::pack_of(data.key))};
|
||||
@@ -1166,15 +1167,62 @@ bool terrain_streamer::paint(double X, double Z, double Radius, double Strength,
|
||||
{
|
||||
if (Radius <= 0.0 || Strength <= 0.0)
|
||||
return false;
|
||||
return paint_area(glm::dvec2{X - Radius, Z - Radius}, glm::dvec2{X + Radius, Z + Radius}, Layer, false, [&](double const Px, double const Pz) {
|
||||
auto const distance{std::sqrt((Px - X) * (Px - X) + (Pz - Z) * (Pz - Z))};
|
||||
return distance > Radius ? 0.0 : Strength * falloff(distance, Radius);
|
||||
});
|
||||
}
|
||||
|
||||
bool terrain_streamer::fill(std::vector<glm::dvec2> const &Outline, double Feather, std::uint16_t Layer, double Strength)
|
||||
{
|
||||
if (Outline.size() < 3 || Strength <= 0.0)
|
||||
return false;
|
||||
Feather = std::max(0.0, Feather);
|
||||
glm::dvec2 low{std::numeric_limits<double>::max()}, high{-std::numeric_limits<double>::max()};
|
||||
for (auto const &point : Outline)
|
||||
{
|
||||
low = glm::min(low, point);
|
||||
high = glm::max(high, point);
|
||||
}
|
||||
// the blend runs across the outline, half of it inside
|
||||
auto const margin{Feather * 0.5};
|
||||
return paint_area(low - glm::dvec2{margin}, high + glm::dvec2{margin}, Layer, true, [&](double const Px, double const Pz) {
|
||||
glm::dvec2 const point{Px, Pz};
|
||||
bool inside{false};
|
||||
auto nearest{std::numeric_limits<double>::max()};
|
||||
for (std::size_t i = 0, j = Outline.size() - 1; i < Outline.size(); j = i++)
|
||||
{
|
||||
auto const &a{Outline[i]};
|
||||
auto const &b{Outline[j]};
|
||||
if ((a.y > Pz) != (b.y > Pz) && Px < (b.x - a.x) * (Pz - a.y) / (b.y - a.y) + a.x)
|
||||
inside = !inside;
|
||||
if (Feather > 0.0)
|
||||
{
|
||||
auto const run{b - a};
|
||||
auto const length{glm::dot(run, run)};
|
||||
auto const t{length > 1e-12 ? std::clamp(glm::dot(point - a, run) / length, 0.0, 1.0) : 0.0};
|
||||
nearest = std::min(nearest, glm::length(point - (a + run * t)));
|
||||
}
|
||||
}
|
||||
if (Feather <= 0.0)
|
||||
return inside ? Strength : 0.0;
|
||||
// signed distance from the outline, inside positive, led smoothly through the blend
|
||||
auto const t{std::clamp(((inside ? nearest : -nearest) + margin) / Feather, 0.0, 1.0)};
|
||||
return Strength * t * t * (3.0 - 2.0 * t);
|
||||
});
|
||||
}
|
||||
|
||||
bool terrain_streamer::paint_area(glm::dvec2 const &Min, glm::dvec2 const &Max, std::uint16_t const Layer, bool const Load, std::function<double(double X, double Z)> const &Amount)
|
||||
{
|
||||
bool changed{false};
|
||||
for (auto *chunk : chunks_in(glm::dvec2{X - Radius, Z - Radius}, glm::dvec2{X + Radius, Z + Radius}, false))
|
||||
for (auto *chunk : chunks_in(Min, Max, Load))
|
||||
{
|
||||
auto const ¤t{chunk->data()};
|
||||
auto const samples{current.paint > 0 ? current.paint : heightmap::default_paint_samples};
|
||||
auto const step{heightmap::chunk_size / samples};
|
||||
auto const low{current.corner()};
|
||||
auto const xs{index_range(low.x, step, samples + 1, X - Radius, X + Radius)};
|
||||
auto const zs{index_range(low.y, step, samples + 1, Z - Radius, Z + Radius)};
|
||||
auto const xs{index_range(low.x, step, samples + 1, Min.x, Max.x)};
|
||||
auto const zs{index_range(low.y, step, samples + 1, Min.y, Max.y)};
|
||||
if (xs.first > xs.second || zs.first > zs.second)
|
||||
continue;
|
||||
if (current.paint <= 0 && current.layers[0] == Layer)
|
||||
@@ -1203,13 +1251,11 @@ bool terrain_streamer::paint(double X, double Z, double Radius, double Strength,
|
||||
for (int iz = zs.first; iz <= zs.second; ++iz)
|
||||
for (int ix = xs.first; ix <= xs.second; ++ix)
|
||||
{
|
||||
auto const px{low.x + ix * step}, pz{low.y + iz * step};
|
||||
auto const distance{std::sqrt((px - X) * (px - X) + (pz - Z) * (pz - Z))};
|
||||
if (distance > Radius)
|
||||
auto const amount{std::clamp(Amount(low.x + ix * step, low.y + iz * step), 0.0, 1.0)};
|
||||
if (amount <= 0.0)
|
||||
continue;
|
||||
auto const sample{static_cast<std::size_t>(iz) * (samples + 1) + ix};
|
||||
auto &own{data->weights[slot * plane + sample]};
|
||||
auto const amount{std::clamp(Strength * falloff(distance, Radius), 0.0, 1.0)};
|
||||
auto const target{static_cast<int>(std::lround(own + (255 - own) * amount))};
|
||||
if (target == own)
|
||||
continue;
|
||||
@@ -1239,13 +1285,13 @@ bool terrain_streamer::paint(double X, double Z, double Radius, double Strength,
|
||||
return changed;
|
||||
}
|
||||
|
||||
std::uint16_t terrain_streamer::layer(std::string const &Material, float const Size)
|
||||
std::uint16_t terrain_streamer::layer(std::string const &Material, float const Size, float const Rotation)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_manifest.layers.size(); ++i)
|
||||
if (m_manifest.layers[i].material == Material && (Size <= 0.f || m_manifest.layers[i].size == Size))
|
||||
if (m_manifest.layers[i].material == Material && (Size <= 0.f || m_manifest.layers[i].size == Size) && m_manifest.layers[i].rotation == Rotation)
|
||||
return static_cast<std::uint16_t>(i);
|
||||
remember_manifest();
|
||||
m_manifest.layers.push_back({Material, std::max(0.f, Size)});
|
||||
m_manifest.layers.push_back({Material, std::max(0.f, Size), Rotation});
|
||||
m_manifestchanged = true;
|
||||
return static_cast<std::uint16_t>(m_manifest.layers.size() - 1);
|
||||
}
|
||||
@@ -1263,6 +1309,19 @@ void terrain_streamer::layer_size(std::uint16_t const Layer, float const Size)
|
||||
entry.second->m_paintedversion = 0;
|
||||
}
|
||||
|
||||
void terrain_streamer::layer_rotation(std::uint16_t const Layer, float Rotation)
|
||||
{
|
||||
Rotation = static_cast<float>(std::remainder(static_cast<double>(Rotation), 360.0));
|
||||
if (Layer >= m_manifest.layers.size() || m_manifest.layers[Layer].rotation == Rotation)
|
||||
return;
|
||||
remember_manifest();
|
||||
m_manifest.layers[Layer].rotation = Rotation;
|
||||
m_manifestchanged = true;
|
||||
for (auto &entry : m_chunks)
|
||||
if (entry.second->data().slot_of(Layer) >= 0 || (Layer == 0 && entry.second->data().layer_count() == 0))
|
||||
entry.second->m_paintedversion = 0;
|
||||
}
|
||||
|
||||
void terrain_streamer::layer_material(std::uint16_t const Layer, std::string const &Material)
|
||||
{
|
||||
if (Layer >= m_manifest.layers.size() || Material.empty() || m_manifest.layers[Layer].material == Material)
|
||||
@@ -1529,7 +1588,7 @@ terrain_streamer::change terrain_streamer::restore(change const &Change)
|
||||
opposite.water_material = m_manifest.water_material;
|
||||
opposite.modifiers = m_manifest.modifiers;
|
||||
auto const samelayers{std::equal(Change.layers.begin(), Change.layers.end(), m_manifest.layers.begin(), m_manifest.layers.end(),
|
||||
[](heightmap::layer_def const &A, heightmap::layer_def const &B) { return A.material == B.material && A.size == B.size; })};
|
||||
[](heightmap::layer_def const &A, heightmap::layer_def const &B) { return A.material == B.material && A.size == B.size && A.rotation == B.rotation; })};
|
||||
m_manifest.layers = Change.layers;
|
||||
m_manifest.modifiers = Change.modifiers;
|
||||
m_manifest.water = Change.water;
|
||||
|
||||
@@ -112,9 +112,15 @@ class terrain_streamer
|
||||
std::size_t touched_up() const;
|
||||
// paints the material of the palette entry Layer within Radius of (X,Z), Strength (0..1) of the way at the centre
|
||||
bool paint(double X, double Z, double Radius, double Strength, std::uint16_t Layer);
|
||||
// palette entry of the material, added to the palette if needed
|
||||
std::uint16_t layer(std::string const &Material, float Size = 0.f);
|
||||
// fills the area within Outline (x, z) with the material of the palette entry Layer, Strength (0..1) of the way.
|
||||
// Feather: metres across the outline the material blends in over, half of them inside; 0: a sharp edge, as sharp as
|
||||
// the paint samples of the chunks go. the chunks are loaded where needed
|
||||
bool fill(std::vector<glm::dvec2> const &Outline, double Feather, std::uint16_t Layer, double Strength = 1.0);
|
||||
// palette entry of the material (repeated at Size, 0: any, turned by Rotation degrees), added to the palette if needed
|
||||
std::uint16_t layer(std::string const &Material, float Size = 0.f, float Rotation = 0.f);
|
||||
void layer_size(std::uint16_t Layer, float Size);
|
||||
// turns the textures of the palette entry Layer by Rotation degrees, on every chunk painted with it
|
||||
void layer_rotation(std::uint16_t Layer, float Rotation);
|
||||
// gives the palette entry Layer another material; the chunks painted with it change with it
|
||||
void layer_material(std::uint16_t Layer, std::string const &Material);
|
||||
void compress(bool State);
|
||||
@@ -234,6 +240,9 @@ class terrain_streamer
|
||||
heightmap::chunk_ptr neighbour(chunk_key const &Key) const;
|
||||
void touch_neighbours(chunk_key const &Key);
|
||||
material_handle palette_material(std::uint16_t const Layer);
|
||||
// paints the material of the palette entry Layer over the paint samples within the rectangle, Amount (0..1) of the way
|
||||
// at each, as Amount gives it for the place of the sample. Load: chunks not in memory are loaded
|
||||
bool paint_area(glm::dvec2 const &Min, glm::dvec2 const &Max, std::uint16_t Layer, bool Load, std::function<double(double X, double Z)> const &Amount);
|
||||
// chunk loaded right away from its pack file, for editing far from the camera
|
||||
editor_terrain *load_now(chunk_key const &Key);
|
||||
// chunks in memory or in the files overlapping the rectangle, loaded if needed
|
||||
|
||||
Reference in New Issue
Block a user