mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 05:09:20 +02:00
feat(editor): orthophoto lift above the ground and opacity in the scene
This commit is contained in:
@@ -1174,6 +1174,7 @@ void editor_mode::load_orthophoto_settings()
|
|||||||
config.year = stored.year;
|
config.year = stored.year;
|
||||||
config.hires = stored.hires;
|
config.hires = stored.hires;
|
||||||
config.drape = stored.drape;
|
config.drape = stored.drape;
|
||||||
|
config.lift = stored.lift;
|
||||||
config.in_scene = stored.in_scene;
|
config.in_scene = stored.in_scene;
|
||||||
config.north = 0.0;
|
config.north = 0.0;
|
||||||
config.east = 0.0;
|
config.east = 0.0;
|
||||||
@@ -1192,6 +1193,7 @@ void editor_mode::save_orthophoto_settings()
|
|||||||
stored.year = config.year;
|
stored.year = config.year;
|
||||||
stored.hires = config.hires;
|
stored.hires = config.hires;
|
||||||
stored.drape = config.drape;
|
stored.drape = config.drape;
|
||||||
|
stored.lift = config.lift;
|
||||||
stored.in_scene = config.in_scene;
|
stored.in_scene = config.in_scene;
|
||||||
if (config.north != 0.0 || config.east != 0.0)
|
if (config.north != 0.0 || config.east != 0.0)
|
||||||
EditorSettings.orthophoto_origin(m_orthophoto_scenery, config.north, config.east);
|
EditorSettings.orthophoto_origin(m_orthophoto_scenery, config.north, config.east);
|
||||||
@@ -1267,6 +1269,15 @@ void editor_mode::render_orthophoto_ui()
|
|||||||
ImGui::SetTooltip("Samples the ground again, after it was edited");
|
ImGui::SetTooltip("Samples the ground again, after it was edited");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
begin_disabled(!config.drape);
|
||||||
|
ImGui::SetNextItemWidth(160.0f);
|
||||||
|
changed |= ImGui::DragFloat("Lift above ground (m)", &config.lift, 0.01f, 0.0f, 50.0f, "%.2f");
|
||||||
|
persist |= ImGui::IsItemDeactivatedAfterEdit();
|
||||||
|
end_disabled(!config.drape);
|
||||||
|
if (ImGui::IsItemHovered())
|
||||||
|
ImGui::SetTooltip("Raises the fitted imagery above the ground. Keep it below the objects you want to see:\n"
|
||||||
|
"in the scene everything lower than this ends up under the imagery.");
|
||||||
|
|
||||||
begin_disabled(config.drape);
|
begin_disabled(config.drape);
|
||||||
ImGui::SetNextItemWidth(160.0f);
|
ImGui::SetNextItemWidth(160.0f);
|
||||||
changed |= ImGui::DragFloat("Height (m)", &config.height, 0.1f, -1000.0f, 3000.0f, "%.2f");
|
changed |= ImGui::DragFloat("Height (m)", &config.height, 0.1f, -1000.0f, 3000.0f, "%.2f");
|
||||||
@@ -1277,14 +1288,16 @@ void editor_mode::render_orthophoto_ui()
|
|||||||
changed = persist = true;
|
changed = persist = true;
|
||||||
if (ImGui::IsItemHovered())
|
if (ImGui::IsItemHovered())
|
||||||
ImGui::SetTooltip("Draws the imagery as part of the scene, so tracks, models and terrain in front of it hide it.\n"
|
ImGui::SetTooltip("Draws the imagery as part of the scene, so tracks, models and terrain in front of it hide it.\n"
|
||||||
"It is opaque then and lit like the scene; mouse placement lands on it as on any surface.\n"
|
"It is lit like the scene. Fully opaque, mouse placement lands on it as on any surface;\n"
|
||||||
|
"translucent, it doesn't catch the mouse.\n"
|
||||||
"Not available with the Better Renderer, which doesn't pick up geometry added in the editor.");
|
"Not available with the Better Renderer, which doesn't pick up geometry added in the editor.");
|
||||||
|
|
||||||
begin_disabled(config.in_scene);
|
|
||||||
ImGui::SetNextItemWidth(160.0f);
|
ImGui::SetNextItemWidth(160.0f);
|
||||||
changed |= ImGui::SliderFloat("Opacity", &config.opacity, 0.0f, 1.0f, "%.2f");
|
changed |= ImGui::SliderFloat("Opacity", &config.opacity, 0.0f, 1.0f, "%.2f");
|
||||||
persist |= ImGui::IsItemDeactivatedAfterEdit();
|
persist |= ImGui::IsItemDeactivatedAfterEdit();
|
||||||
end_disabled(config.in_scene);
|
if (config.in_scene && ImGui::IsItemHovered())
|
||||||
|
ImGui::SetTooltip("In the scene the opacity is part of the textures: the tiles are read again from the cache\n"
|
||||||
|
"shortly after the slider stops, which takes a moment.");
|
||||||
|
|
||||||
// newest imagery, or the newest imagery taken up to the end of the selected year
|
// newest imagery, or the newest imagery taken up to the end of the selected year
|
||||||
int const thisyear = static_cast<int>(std::chrono::year_month_day{std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now())}.year());
|
int const thisyear = static_cast<int>(std::chrono::year_month_day{std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now())}.year());
|
||||||
|
|||||||
@@ -554,7 +554,6 @@ template <typename Distance_> int clip_polygon(clip_vertex const *In, int Count,
|
|||||||
// ground fit and in-scene geometry
|
// ground fit and in-scene geometry
|
||||||
|
|
||||||
constexpr int ground_grid{32}; // ground samples per tile side (8 m) for the draped overlay
|
constexpr int ground_grid{32}; // ground samples per tile side (8 m) for the draped overlay
|
||||||
constexpr double surface_offset{0.05}; // lift of the in-scene imagery above the ground it covers, against z-fighting
|
|
||||||
constexpr double steepest_ground{0.3}; // minimal normal.y of ground triangles receiving the imagery
|
constexpr double steepest_ground{0.3}; // minimal normal.y of ground triangles receiving the imagery
|
||||||
constexpr float no_height{-std::numeric_limits<float>::max()};
|
constexpr float no_height{-std::numeric_limits<float>::max()};
|
||||||
|
|
||||||
@@ -684,8 +683,9 @@ void editor_orthophoto::enabled(bool State)
|
|||||||
void editor_orthophoto::settings(config const &Config)
|
void editor_orthophoto::settings(config const &Config)
|
||||||
{
|
{
|
||||||
bool const sourcechanged = (Config.year != m_config.year || Config.hires != m_config.hires);
|
bool const sourcechanged = (Config.year != m_config.year || Config.hires != m_config.hires);
|
||||||
bool const shapechanged = (Config.height != m_config.height || Config.drape != m_config.drape || Config.in_scene != m_config.in_scene);
|
bool const shapechanged = (Config.height != m_config.height || Config.drape != m_config.drape || Config.in_scene != m_config.in_scene || Config.lift != m_config.lift);
|
||||||
m_config = Config;
|
m_config = Config;
|
||||||
|
m_config.lift = std::clamp(m_config.lift, 0.0f, 50.0f);
|
||||||
m_config.radius = std::clamp(m_config.radius, 0, max_radius);
|
m_config.radius = std::clamp(m_config.radius, 0, max_radius);
|
||||||
m_config.opacity = std::clamp(m_config.opacity, 0.0f, 1.0f);
|
m_config.opacity = std::clamp(m_config.opacity, 0.0f, 1.0f);
|
||||||
if (shapechanged)
|
if (shapechanged)
|
||||||
@@ -812,17 +812,24 @@ void editor_orthophoto::release_tile(tile &Tile)
|
|||||||
Tile.slot = texture_slot{};
|
Tile.slot = texture_slot{};
|
||||||
}
|
}
|
||||||
|
|
||||||
void editor_orthophoto::schedule(tile_key const &Key, tile &Tile, int Level)
|
void editor_orthophoto::schedule(tile_key const &Key, tile &Tile, int Level, std::uint8_t Alpha)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
m_queue.erase(std::remove_if(m_queue.begin(), m_queue.end(), [&](job const &Job) { return Job.key == Key; }), m_queue.end());
|
m_queue.erase(std::remove_if(m_queue.begin(), m_queue.end(), [&](job const &Job) { return Job.key == Key; }), m_queue.end());
|
||||||
m_queue.push_back(job{Key, Level, source{m_config.year, m_config.hires}, m_generation.load()});
|
m_queue.push_back(job{Key, Level, Alpha, source{m_config.year, m_config.hires}, m_generation.load()});
|
||||||
}
|
}
|
||||||
Tile.requested = Level;
|
Tile.requested = Level;
|
||||||
|
Tile.requested_alpha = Alpha;
|
||||||
m_wakeup.notify_one();
|
m_wakeup.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::uint8_t editor_orthophoto::texture_alpha() const
|
||||||
|
{
|
||||||
|
// the overlay applies its opacity when drawing, the scene shapes can only take it from the texture
|
||||||
|
return m_config.in_scene ? static_cast<std::uint8_t>(std::lround(m_config.opacity * 255.0f)) : 255;
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t editor_orthophoto::apply(result &Result, double Now)
|
std::size_t editor_orthophoto::apply(result &Result, double Now)
|
||||||
{
|
{
|
||||||
if (Result.generation != m_generation.load())
|
if (Result.generation != m_generation.load())
|
||||||
@@ -831,7 +838,7 @@ std::size_t editor_orthophoto::apply(result &Result, double Now)
|
|||||||
if (lookup == m_tiles.end())
|
if (lookup == m_tiles.end())
|
||||||
return 0; // left the area in the meantime; the imagery stays in the disk cache
|
return 0; // left the area in the meantime; the imagery stays in the disk cache
|
||||||
tile &t = lookup->second;
|
tile &t = lookup->second;
|
||||||
bool const current = (t.requested == Result.level);
|
bool const current = (t.requested == Result.level && t.requested_alpha == Result.alpha);
|
||||||
if (current)
|
if (current)
|
||||||
t.requested = 0;
|
t.requested = 0;
|
||||||
|
|
||||||
@@ -847,6 +854,9 @@ std::size_t editor_orthophoto::apply(result &Result, double Now)
|
|||||||
|
|
||||||
t.failures = 0;
|
t.failures = 0;
|
||||||
t.level = Result.level;
|
t.level = Result.level;
|
||||||
|
if ((t.alpha < 255) != (Result.alpha < 255))
|
||||||
|
t.scene_dirty = true; // the shape moves between the opaque and the translucent pass
|
||||||
|
t.alpha = Result.alpha;
|
||||||
t.empty = Result.empty;
|
t.empty = Result.empty;
|
||||||
if (Result.empty)
|
if (Result.empty)
|
||||||
{
|
{
|
||||||
@@ -974,7 +984,7 @@ void editor_orthophoto::remove_shape(tile &Tile)
|
|||||||
return;
|
return;
|
||||||
if (m_region == simulation::Region)
|
if (m_region == simulation::Region)
|
||||||
{
|
{
|
||||||
auto &shapes = Tile.section->m_shapes;
|
auto &shapes = (Tile.cell != nullptr ? Tile.cell->m_shapestranslucent : Tile.section->m_shapes);
|
||||||
for (auto it = shapes.begin(); it != shapes.end(); ++it)
|
for (auto it = shapes.begin(); it != shapes.end(); ++it)
|
||||||
{
|
{
|
||||||
auto const &geometry = it->data().geometry;
|
auto const &geometry = it->data().geometry;
|
||||||
@@ -986,6 +996,7 @@ void editor_orthophoto::remove_shape(tile &Tile)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Tile.section = nullptr;
|
Tile.section = nullptr;
|
||||||
|
Tile.cell = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void editor_orthophoto::release_geometry(tile &Tile)
|
void editor_orthophoto::release_geometry(tile &Tile)
|
||||||
@@ -1024,9 +1035,9 @@ void editor_orthophoto::build_shape(tile_key const &Key, tile &Tile)
|
|||||||
{
|
{
|
||||||
vertices.reserve(Tile.surface.size());
|
vertices.reserve(Tile.surface.size());
|
||||||
for (auto const &point : Tile.surface)
|
for (auto const &point : Tile.surface)
|
||||||
add(point, surface_offset);
|
add(point, m_config.lift);
|
||||||
ylo = Tile.height_min;
|
ylo = Tile.height_min;
|
||||||
yhi = Tile.height_max + surface_offset;
|
yhi = Tile.height_max + m_config.lift;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1042,7 +1053,11 @@ void editor_orthophoto::build_shape(tile_key const &Key, tile &Tile)
|
|||||||
glm::dvec3 const centre(corner.x - tile_size * 0.5, (ylo + yhi) * 0.5, corner.y - tile_size * 0.5);
|
glm::dvec3 const centre(corner.x - tile_size * 0.5, (ylo + yhi) * 0.5, corner.y - tile_size * 0.5);
|
||||||
scene::basic_section §ion = simulation::Region->section(centre);
|
scene::basic_section §ion = simulation::Region->section(centre);
|
||||||
section.create_geometry(); // existing section geometry has to be in place before we add ours (idempotent)
|
section.create_geometry(); // existing section geometry has to be in place before we add ours (idempotent)
|
||||||
glm::dvec3 const origin = section.m_area.center;
|
// opaque imagery goes with the section geometry; translucent one has to be drawn in the translucent pass,
|
||||||
|
// which only covers the cells. shapes are drawn relative to the centre of their container
|
||||||
|
bool const translucent = (Tile.alpha < 255);
|
||||||
|
scene::basic_cell *cell = translucent ? §ion.cell(centre) : nullptr;
|
||||||
|
glm::dvec3 const origin = translucent ? cell->m_area.center : section.m_area.center;
|
||||||
|
|
||||||
// chunks are padded to a few fixed sizes (whole triangles), so ones freed by other tiles can be refilled in place
|
// chunks are padded to a few fixed sizes (whole triangles), so ones freed by other tiles can be refilled in place
|
||||||
std::size_t capacity{6};
|
std::size_t capacity{6};
|
||||||
@@ -1096,12 +1111,22 @@ void editor_orthophoto::build_shape(tile_key const &Key, tile &Tile)
|
|||||||
scene::shape_node shape;
|
scene::shape_node shape;
|
||||||
shape.make_terrain(Tile.slot.material, std::move(bounds), origin);
|
shape.make_terrain(Tile.slot.material, std::move(bounds), origin);
|
||||||
shape.geometry(Tile.geometry);
|
shape.geometry(Tile.geometry);
|
||||||
section.m_shapes.emplace_back(std::move(shape));
|
// extend the bounds of the containers so the tile isn't culled at its edges
|
||||||
// extend the section bounds so the tile isn't culled at its edges
|
|
||||||
double const reach = glm::length(glm::dvec3(tile_size * 0.5, (yhi - ylo) * 0.5, tile_size * 0.5));
|
double const reach = glm::length(glm::dvec3(tile_size * 0.5, (yhi - ylo) * 0.5, tile_size * 0.5));
|
||||||
section.m_area.radius = std::max(section.m_area.radius, static_cast<float>(glm::length(section.m_area.center - centre) + reach));
|
section.m_area.radius = std::max(section.m_area.radius, static_cast<float>(glm::length(section.m_area.center - centre) + reach));
|
||||||
|
if (cell != nullptr)
|
||||||
|
{
|
||||||
|
cell->m_shapestranslucent.emplace_back(std::move(shape));
|
||||||
|
cell->m_area.radius = std::max(cell->m_area.radius, static_cast<float>(glm::length(cell->m_area.center - centre) + reach));
|
||||||
|
cell->m_active = true; // the renderer skips cells which held nothing so far
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
section.m_shapes.emplace_back(std::move(shape));
|
||||||
|
}
|
||||||
|
|
||||||
Tile.section = §ion;
|
Tile.section = §ion;
|
||||||
|
Tile.cell = cell;
|
||||||
m_region = simulation::Region;
|
m_region = simulation::Region;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1171,6 +1196,16 @@ void editor_orthophoto::update(glm::dvec3 const &Camera)
|
|||||||
int const radius = m_config.radius;
|
int const radius = m_config.radius;
|
||||||
auto const distance = [&](tile_key const &Key) { return std::max(std::abs(Key.first - centre.first), std::abs(Key.second - centre.second)); };
|
auto const distance = [&](tile_key const &Key) { return std::max(std::abs(Key.first - centre.first), std::abs(Key.second - centre.second)); };
|
||||||
|
|
||||||
|
// opacity of the scene shapes: the tiles are read again with the new alpha once the slider stops moving
|
||||||
|
std::uint8_t const alpha = texture_alpha();
|
||||||
|
if (alpha != m_alpha_pending)
|
||||||
|
{
|
||||||
|
m_alpha_pending = alpha;
|
||||||
|
m_alpha_changed = now;
|
||||||
|
}
|
||||||
|
if (m_alpha != m_alpha_pending && now - m_alpha_changed > 0.3)
|
||||||
|
m_alpha = m_alpha_pending;
|
||||||
|
|
||||||
// tiles which left the area
|
// tiles which left the area
|
||||||
bool evicted{false};
|
bool evicted{false};
|
||||||
for (auto it = m_tiles.begin(); it != m_tiles.end();)
|
for (auto it = m_tiles.begin(); it != m_tiles.end();)
|
||||||
@@ -1199,9 +1234,9 @@ void editor_orthophoto::update(glm::dvec3 const &Camera)
|
|||||||
if (!in_service_area(key.first, key.second))
|
if (!in_service_area(key.first, key.second))
|
||||||
t.empty = true;
|
t.empty = true;
|
||||||
int const level = level_for(std::max(std::abs(dx), std::abs(dy)), m_config.hires);
|
int const level = level_for(std::max(std::abs(dx), std::abs(dy)), m_config.hires);
|
||||||
if (t.empty || t.level == level || t.requested == level || now < t.retry_at)
|
if (t.empty || (t.level == level && t.alpha == m_alpha) || (t.requested == level && t.requested_alpha == m_alpha) || now < t.retry_at)
|
||||||
continue;
|
continue;
|
||||||
schedule(key, t, level);
|
schedule(key, t, level, m_alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ground fit, one tile per frame (it walks the scene geometry), nearest first
|
// ground fit, one tile per frame (it walks the scene geometry), nearest first
|
||||||
@@ -1265,6 +1300,7 @@ void editor_orthophoto::worker()
|
|||||||
out.key = current.key;
|
out.key = current.key;
|
||||||
out.level = current.level;
|
out.level = current.level;
|
||||||
out.generation = current.generation;
|
out.generation = current.generation;
|
||||||
|
out.alpha = current.alpha;
|
||||||
|
|
||||||
image picture;
|
image picture;
|
||||||
std::string error;
|
std::string error;
|
||||||
@@ -1274,6 +1310,9 @@ void editor_orthophoto::worker()
|
|||||||
return;
|
return;
|
||||||
if (!out.ok)
|
if (!out.ok)
|
||||||
ErrorLog("Editor orthophoto: tile " + std::to_string(current.key.second) + "_" + std::to_string(current.key.first) + " failed: " + error);
|
ErrorLog("Editor orthophoto: tile " + std::to_string(current.key.second) + "_" + std::to_string(current.key.first) + " failed: " + error);
|
||||||
|
if (out.ok && current.alpha < 255)
|
||||||
|
for (std::size_t i = 3; i < picture.rgba.size(); i += 4)
|
||||||
|
picture.rgba[i] = static_cast<std::uint8_t>((picture.rgba[i] * current.alpha + 127u) / 255u);
|
||||||
out.width = picture.width;
|
out.width = picture.width;
|
||||||
out.height = picture.height;
|
out.height = picture.height;
|
||||||
out.pixels = std::move(picture.rgba);
|
out.pixels = std::move(picture.rgba);
|
||||||
@@ -1320,8 +1359,9 @@ void editor_orthophoto::draw(glm::mat4 const &ViewProjection, glm::dvec3 const &
|
|||||||
|
|
||||||
bool const draped = m_config.drape && !t.heights.empty();
|
bool const draped = m_config.drape && !t.heights.empty();
|
||||||
double const flat = m_config.height;
|
double const flat = m_config.height;
|
||||||
double const ylo = draped ? t.height_min : flat;
|
double const lift = m_config.lift;
|
||||||
double const yhi = draped ? t.height_max : flat;
|
double const ylo = draped ? t.height_min + lift : flat;
|
||||||
|
double const yhi = draped ? t.height_max + lift : flat;
|
||||||
|
|
||||||
// world extent of the tile; u runs west->east (-x), v runs north->south (-z), matching the image rows
|
// world extent of the tile; u runs west->east (-x), v runs north->south (-z), matching the image rows
|
||||||
glm::dvec2 const corner = tile_corner(entry.first);
|
glm::dvec2 const corner = tile_corner(entry.first);
|
||||||
@@ -1346,7 +1386,7 @@ void editor_orthophoto::draw(glm::mat4 const &ViewProjection, glm::dvec3 const &
|
|||||||
if (draped)
|
if (draped)
|
||||||
segments = std::max(segments, distance < 300.0 ? ground_grid : distance < 800.0 ? ground_grid / 2 : ground_grid / 4);
|
segments = std::max(segments, distance < 300.0 ? ground_grid : distance < 800.0 ? ground_grid / 2 : ground_grid / 4);
|
||||||
float const segmentsize = 1.0f / static_cast<float>(segments);
|
float const segmentsize = 1.0f / static_cast<float>(segments);
|
||||||
auto const height_at = [&](float U, float V) { return draped ? sample_heights(t.heights, U, V) : flat; };
|
auto const height_at = [&](float U, float V) { return draped ? sample_heights(t.heights, U, V) + lift : flat; };
|
||||||
|
|
||||||
drawlist->PushTextureID(reinterpret_cast<ImTextureID>(textureid));
|
drawlist->PushTextureID(reinterpret_cast<ImTextureID>(textureid));
|
||||||
for (int j = 0; j < segments; ++j)
|
for (int j = 0; j < segments; ++j)
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ namespace scene
|
|||||||
{
|
{
|
||||||
class basic_region;
|
class basic_region;
|
||||||
class basic_section;
|
class basic_section;
|
||||||
|
class basic_cell;
|
||||||
} // namespace scene
|
} // namespace scene
|
||||||
|
|
||||||
// Editor aid: aerial orthophoto tiles from geoportal.gov.pl (GUGiK WMS), shown around the camera either
|
// Editor aid: aerial orthophoto tiles from geoportal.gov.pl (GUGiK WMS), shown around the camera either
|
||||||
@@ -43,8 +44,9 @@ class basic_section;
|
|||||||
// Downloading, cache I/O and image decoding run on worker threads; the main thread only uploads
|
// Downloading, cache I/O and image decoding run on worker threads; the main thread only uploads
|
||||||
// finished images (a bounded amount per frame) and emits the geometry. By default the layer goes into
|
// finished images (a bounded amount per frame) and emits the geometry. By default the layer goes into
|
||||||
// the ImGui background draw list, like the other editor viewport overlays: translucent and drawn over
|
// the ImGui background draw list, like the other editor viewport overlays: translucent and drawn over
|
||||||
// everything. Optionally it is put into the scene instead (opaque shapes in the region sections, like the
|
// everything. Optionally it is put into the scene instead (shapes in the region sections, like the editor
|
||||||
// editor terrain), so tracks, models and terrain in front of it cover it.
|
// terrain, or translucent shapes in the cells when it isn't fully opaque), so tracks, models and terrain in
|
||||||
|
// front of it cover it.
|
||||||
class editor_orthophoto
|
class editor_orthophoto
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -54,10 +56,11 @@ class editor_orthophoto
|
|||||||
double east{0.0}; // PUWG 1992 Y (easting) of the scenery origin
|
double east{0.0}; // PUWG 1992 Y (easting) of the scenery origin
|
||||||
int radius{2}; // tiles loaded around the camera tile (Chebyshev distance)
|
int radius{2}; // tiles loaded around the camera tile (Chebyshev distance)
|
||||||
float height{0.0f}; // world Y of the layer (with drape: only where no ground was found)
|
float height{0.0f}; // world Y of the layer (with drape: only where no ground was found)
|
||||||
float opacity{0.6f}; // 0..1, overlay only
|
float opacity{0.6f}; // 0..1
|
||||||
int year{0}; // 0 = newest available imagery, otherwise newest imagery taken up to the end of that year
|
int year{0}; // 0 = newest available imagery, otherwise newest imagery taken up to the end of that year
|
||||||
bool hires{false}; // 4096 px tiles from the high-resolution product next to the camera, where it exists
|
bool hires{false}; // 4096 px tiles from the high-resolution product next to the camera, where it exists
|
||||||
bool drape{true}; // fit the imagery onto the ground geometry instead of a flat plane
|
bool drape{true}; // fit the imagery onto the ground geometry instead of a flat plane
|
||||||
|
float lift{0.05f}; // with drape: distance above the ground (in the scene it has to clear the ground to be seen)
|
||||||
bool in_scene{false}; // render as opaque scene geometry, covered by whatever is in front of it
|
bool in_scene{false}; // render as opaque scene geometry, covered by whatever is in front of it
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -122,7 +125,8 @@ class editor_orthophoto
|
|||||||
struct job
|
struct job
|
||||||
{
|
{
|
||||||
tile_key key;
|
tile_key key;
|
||||||
int level{0}; // requested texture size in pixels
|
int level{0}; // requested texture size in pixels
|
||||||
|
std::uint8_t alpha{255}; // baked into the texture, for translucency in the scene
|
||||||
source src;
|
source src;
|
||||||
std::uint32_t generation{0};
|
std::uint32_t generation{0};
|
||||||
};
|
};
|
||||||
@@ -131,6 +135,7 @@ class editor_orthophoto
|
|||||||
{
|
{
|
||||||
tile_key key;
|
tile_key key;
|
||||||
int level{0};
|
int level{0};
|
||||||
|
std::uint8_t alpha{255};
|
||||||
std::uint32_t generation{0};
|
std::uint32_t generation{0};
|
||||||
bool ok{false};
|
bool ok{false};
|
||||||
bool empty{false}; // no imagery for this place/year: nothing to draw
|
bool empty{false}; // no imagery for this place/year: nothing to draw
|
||||||
@@ -151,6 +156,8 @@ class editor_orthophoto
|
|||||||
texture_slot slot;
|
texture_slot slot;
|
||||||
int level{0}; // size of the imagery currently held (0 = nothing yet)
|
int level{0}; // size of the imagery currently held (0 = nothing yet)
|
||||||
int requested{0}; // level of the job scheduled for this tile (0 = none)
|
int requested{0}; // level of the job scheduled for this tile (0 = none)
|
||||||
|
std::uint8_t alpha{255}; // alpha of the imagery currently held
|
||||||
|
std::uint8_t requested_alpha{255}; // alpha of the scheduled job
|
||||||
bool empty{false};
|
bool empty{false};
|
||||||
int failures{0};
|
int failures{0};
|
||||||
double retry_at{0.0}; // steady clock seconds
|
double retry_at{0.0}; // steady clock seconds
|
||||||
@@ -162,6 +169,7 @@ class editor_orthophoto
|
|||||||
std::vector<glm::dvec3> surface; // upward facing ground triangles clipped to the tile (3 vertices each)
|
std::vector<glm::dvec3> surface; // upward facing ground triangles clipped to the tile (3 vertices each)
|
||||||
// scene geometry
|
// scene geometry
|
||||||
scene::basic_section *section{nullptr}; // section holding the shape (nullptr = not in the scene)
|
scene::basic_section *section{nullptr}; // section holding the shape (nullptr = not in the scene)
|
||||||
|
scene::basic_cell *cell{nullptr}; // set instead of the section's shape list when the shape is translucent
|
||||||
gfx::geometry_handle geometry{0, 0};
|
gfx::geometry_handle geometry{0, 0};
|
||||||
std::size_t capacity{0}; // vertex count of the geometry chunk
|
std::size_t capacity{0}; // vertex count of the geometry chunk
|
||||||
bool scene_dirty{true}; // the shape needs to be rebuilt
|
bool scene_dirty{true}; // the shape needs to be rebuilt
|
||||||
@@ -170,7 +178,8 @@ class editor_orthophoto
|
|||||||
static int level_for(int Distance, bool Hires);
|
static int level_for(int Distance, bool Hires);
|
||||||
tile_key camera_tile(glm::dvec3 const &Camera) const;
|
tile_key camera_tile(glm::dvec3 const &Camera) const;
|
||||||
glm::dvec2 tile_corner(tile_key const &Key) const; // world (x,z) of the north-west corner
|
glm::dvec2 tile_corner(tile_key const &Key) const; // world (x,z) of the north-west corner
|
||||||
void schedule(tile_key const &Key, tile &Tile, int Level);
|
void schedule(tile_key const &Key, tile &Tile, int Level, std::uint8_t Alpha);
|
||||||
|
std::uint8_t texture_alpha() const; // alpha the textures should carry with the current settings
|
||||||
std::size_t apply(result &Result, double Now); // returns bytes uploaded
|
std::size_t apply(result &Result, double Now); // returns bytes uploaded
|
||||||
void release_tile(tile &Tile);
|
void release_tile(tile &Tile);
|
||||||
texture_slot acquire_slot();
|
texture_slot acquire_slot();
|
||||||
@@ -191,6 +200,10 @@ class editor_orthophoto
|
|||||||
ground_query m_ground;
|
ground_query m_ground;
|
||||||
scene::basic_region *m_region{nullptr}; // region the scene geometry was put into
|
scene::basic_region *m_region{nullptr}; // region the scene geometry was put into
|
||||||
std::multimap<std::size_t, gfx::geometry_handle> m_free_chunks; // geometry chunks by capacity, reused between tiles
|
std::multimap<std::size_t, gfx::geometry_handle> m_free_chunks; // geometry chunks by capacity, reused between tiles
|
||||||
|
// the scene opacity is baked into the textures, which means reading the tiles again; applied once the value settles
|
||||||
|
std::uint8_t m_alpha{255}; // in effect
|
||||||
|
std::uint8_t m_alpha_pending{255}; // latest requested
|
||||||
|
double m_alpha_changed{0.0}; // when the requested value last changed
|
||||||
|
|
||||||
// shared with the workers, guarded by m_mutex
|
// shared with the workers, guarded by m_mutex
|
||||||
mutable std::mutex m_mutex;
|
mutable std::mutex m_mutex;
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ bool editorSettings::load()
|
|||||||
m_orthophoto.hires = (value == "1");
|
m_orthophoto.hires = (value == "1");
|
||||||
else if (key == "ortho_drape")
|
else if (key == "ortho_drape")
|
||||||
m_orthophoto.drape = (value == "1");
|
m_orthophoto.drape = (value == "1");
|
||||||
|
else if (key == "ortho_lift")
|
||||||
|
parse(value, m_orthophoto.lift);
|
||||||
else if (key == "ortho_in_scene")
|
else if (key == "ortho_in_scene")
|
||||||
m_orthophoto.in_scene = (value == "1");
|
m_orthophoto.in_scene = (value == "1");
|
||||||
else if (key.rfind(origin_prefix, 0) == 0)
|
else if (key.rfind(origin_prefix, 0) == 0)
|
||||||
@@ -126,6 +128,7 @@ bool editorSettings::save()
|
|||||||
stream << "ortho_year " << m_orthophoto.year << "\n";
|
stream << "ortho_year " << m_orthophoto.year << "\n";
|
||||||
stream << "ortho_4k " << (m_orthophoto.hires ? 1 : 0) << "\n";
|
stream << "ortho_4k " << (m_orthophoto.hires ? 1 : 0) << "\n";
|
||||||
stream << "ortho_drape " << (m_orthophoto.drape ? 1 : 0) << "\n";
|
stream << "ortho_drape " << (m_orthophoto.drape ? 1 : 0) << "\n";
|
||||||
|
stream << "ortho_lift " << m_orthophoto.lift << "\n";
|
||||||
stream << "ortho_in_scene " << (m_orthophoto.in_scene ? 1 : 0) << "\n";
|
stream << "ortho_in_scene " << (m_orthophoto.in_scene ? 1 : 0) << "\n";
|
||||||
stream << std::fixed << std::setprecision(3);
|
stream << std::fixed << std::setprecision(3);
|
||||||
for (auto const &origin : m_orthophoto_origins)
|
for (auto const &origin : m_orthophoto_origins)
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public:
|
|||||||
int year{0};
|
int year{0};
|
||||||
bool hires{false};
|
bool hires{false};
|
||||||
bool drape{true};
|
bool drape{true};
|
||||||
|
float lift{0.05f};
|
||||||
bool in_scene{false};
|
bool in_scene{false};
|
||||||
};
|
};
|
||||||
orthophoto_settings &orthophoto() { return m_orthophoto; }
|
orthophoto_settings &orthophoto() { return m_orthophoto; }
|
||||||
|
|||||||
Reference in New Issue
Block a user