17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-28 21:37:31 +01:00

feat(editor): heightmap terrain from the terrain model of GUGiK, fitted to the tracks of the scenery

- editorNmt: squares of 500 m of the NMT at 1 m from the WCS service of geoportal.gov.pl, as GeoTIFF, fetched in the
  background, kept in editor/cache/nmt and sampled in the coordinates of the scenery, placed as its orthophoto is
- the terrain panel's Terrain model section: Fit to the tracks moves the model up or down by the median of the
  differences to the formation of the tracks (Biała Podlaska, built at 0: -145.73 m, 0.28 m around it); Along the tracks
  and Around the camera make the chunks as the model comes in; each path gets its formation and slopes 1:1.5 as a modifier
  of the terrain, laid over each other into one corridor, bridges and tunnels keep the model; the orthophoto is turned
  on and laid onto the new ground
- the http client of the orthophoto in editorHttp, shared; fill_from_nearest in editormodeshared.h
- after the generator of Hirek (MaszynaTerrainGenerator): the WCS requests, the cache of squares, the corridor by envelopes
- self-tests nmtfit, nmtmake, nmtwait
This commit is contained in:
maj00r
2026-10-18 12:56:25 +02:00
parent 16348c655a
commit 5af699fba4
12 changed files with 1245 additions and 234 deletions

View File

@@ -9,6 +9,7 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "editor/editorOrthophoto.hpp"
#include "editor/editorHttp.hpp"
#include "rendering/renderer.h"
#include "scene/scene.h"
@@ -19,11 +20,6 @@ http://mozilla.org/MPL/2.0/.
#include "imgui/imgui.h"
#include "stb/stb_image.h"
#if defined(_WIN32)
#include <winhttp.h>
#elif defined(EU07_WITH_CURL)
#include <curl/curl.h>
#endif
#include <algorithm>
#include <chrono>
@@ -49,212 +45,14 @@ constexpr std::size_t upload_budget{16u << 20}; // bytes of texture data uploade
constexpr std::size_t vertex_budget{60000}; // ImGui uses 16-bit indices and not every backend supports vertex offsets
constexpr float near_w{0.5f};
using stop_check = std::function<bool()>;
using stop_check = editor_http::stop_check;
using http_client = editor_http::client;
double steady_seconds()
{
return std::chrono::duration<double>(std::chrono::steady_clock::now().time_since_epoch()).count();
}
// ---------------------------------------------------------------------------------------------
// minimal blocking https GET; one instance per worker thread
class http_client
{
public:
http_client();
~http_client();
http_client(http_client const &) = delete;
http_client &operator=(http_client const &) = delete;
bool get(std::string const &Url, std::vector<std::uint8_t> &Out, std::string &Error, stop_check const &Stop);
private:
#if defined(_WIN32)
HINTERNET m_session{nullptr};
#elif defined(EU07_WITH_CURL)
CURL *m_curl{nullptr};
#endif
};
#if defined(_WIN32)
http_client::http_client()
{
wchar_t const *agent = L"MaSzyna-editor";
#ifdef WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY
m_session = ::WinHttpOpen(agent, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
#endif
if (m_session == nullptr) // automatic proxy needs windows 8.1+
m_session = ::WinHttpOpen(agent, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (m_session != nullptr)
::WinHttpSetTimeouts(m_session, 10000, 10000, 15000, 20000);
}
http_client::~http_client()
{
if (m_session != nullptr)
::WinHttpCloseHandle(m_session);
}
bool http_client::get(std::string const &Url, std::vector<std::uint8_t> &Out, std::string &Error, stop_check const &Stop)
{
Out.clear();
if (m_session == nullptr)
{
Error = "WinHttpOpen failed (" + std::to_string(::GetLastError()) + ")";
return false;
}
std::wstring const url(Url.begin(), Url.end()); // the requests are plain ascii
URL_COMPONENTS parts{};
parts.dwStructSize = sizeof(parts);
parts.dwSchemeLength = static_cast<DWORD>(-1);
parts.dwHostNameLength = static_cast<DWORD>(-1);
parts.dwUrlPathLength = static_cast<DWORD>(-1);
parts.dwExtraInfoLength = static_cast<DWORD>(-1);
if (!::WinHttpCrackUrl(url.c_str(), 0, 0, &parts))
{
Error = "invalid url";
return false;
}
std::wstring const host(parts.lpszHostName, parts.dwHostNameLength);
std::wstring const path = std::wstring(parts.lpszUrlPath, parts.dwUrlPathLength) + std::wstring(parts.lpszExtraInfo, parts.dwExtraInfoLength);
HINTERNET connection = ::WinHttpConnect(m_session, host.c_str(), parts.nPort, 0);
HINTERNET request = connection ? ::WinHttpOpenRequest(connection, L"GET", path.c_str(), nullptr, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES,
parts.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0)
: nullptr;
auto const close = [&]() {
if (request != nullptr)
::WinHttpCloseHandle(request);
if (connection != nullptr)
::WinHttpCloseHandle(connection);
};
if (request == nullptr || !::WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0) || !::WinHttpReceiveResponse(request, nullptr))
{
Error = "WinHTTP error " + std::to_string(::GetLastError());
close();
return false;
}
DWORD status{0};
DWORD statussize{sizeof(status)};
::WinHttpQueryHeaders(request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &status, &statussize, WINHTTP_NO_HEADER_INDEX);
for (;;)
{
if (Stop())
{
Error = "cancelled";
close();
return false;
}
DWORD available{0};
if (!::WinHttpQueryDataAvailable(request, &available))
{
Error = "WinHTTP error " + std::to_string(::GetLastError());
close();
return false;
}
if (available == 0)
break;
std::size_t const offset = Out.size();
Out.resize(offset + available);
DWORD received{0};
if (!::WinHttpReadData(request, Out.data() + offset, available, &received))
{
Error = "WinHTTP error " + std::to_string(::GetLastError());
close();
return false;
}
Out.resize(offset + received);
}
close();
if (status != 200)
{
Error = "HTTP " + std::to_string(status);
return false;
}
return true;
}
#elif defined(EU07_WITH_CURL)
http_client::http_client()
{
static std::once_flag initialized;
std::call_once(initialized, []() { ::curl_global_init(CURL_GLOBAL_DEFAULT); });
m_curl = ::curl_easy_init();
}
http_client::~http_client()
{
if (m_curl != nullptr)
::curl_easy_cleanup(m_curl);
}
bool http_client::get(std::string const &Url, std::vector<std::uint8_t> &Out, std::string &Error, stop_check const &Stop)
{
Out.clear();
if (m_curl == nullptr)
{
Error = "curl_easy_init failed";
return false;
}
auto const write = +[](char *Data, std::size_t Size, std::size_t Count, void *User) -> std::size_t {
auto *out = static_cast<std::vector<std::uint8_t> *>(User);
out->insert(out->end(), Data, Data + Size * Count);
return Size * Count;
};
auto const progress = +[](void *User, curl_off_t, curl_off_t, curl_off_t, curl_off_t) -> int { return (*static_cast<stop_check const *>(User))() ? 1 : 0; };
::curl_easy_reset(m_curl);
::curl_easy_setopt(m_curl, CURLOPT_URL, Url.c_str());
::curl_easy_setopt(m_curl, CURLOPT_USERAGENT, "MaSzyna-editor");
::curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1L);
::curl_easy_setopt(m_curl, CURLOPT_NOSIGNAL, 1L);
::curl_easy_setopt(m_curl, CURLOPT_CONNECTTIMEOUT, 10L);
::curl_easy_setopt(m_curl, CURLOPT_LOW_SPEED_LIMIT, 1L);
::curl_easy_setopt(m_curl, CURLOPT_LOW_SPEED_TIME, 20L);
::curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, write);
::curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &Out);
::curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, 0L);
::curl_easy_setopt(m_curl, CURLOPT_XFERINFOFUNCTION, progress);
::curl_easy_setopt(m_curl, CURLOPT_XFERINFODATA, const_cast<stop_check *>(&Stop));
CURLcode const code = ::curl_easy_perform(m_curl);
if (code != CURLE_OK)
{
Error = ::curl_easy_strerror(code);
return false;
}
long status{0};
::curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &status);
if (status != 200)
{
Error = "HTTP " + std::to_string(status);
return false;
}
return true;
}
#else
http_client::http_client() = default;
http_client::~http_client() = default;
bool http_client::get(std::string const &, std::vector<std::uint8_t> &Out, std::string &Error, stop_check const &)
{
Out.clear();
Error = "no http client in this build";
return false;
}
#endif
} // namespace