diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 479652db..62e78504 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,3 +1,5 @@ +find_package(ZLIB REQUIRED) + file(GLOB PSEMEK_EXAMPLES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*") list(REMOVE_ITEM PSEMEK_EXAMPLES "CMakeLists.txt") @@ -6,6 +8,6 @@ foreach(example ${PSEMEK_EXAMPLES}) set(TARGET_NAME psemek-example-${TARGET_NAME}) psemek_add_executable(${TARGET_NAME} ${example}) if(TARGET ${TARGET_NAME}) - target_link_libraries(${TARGET_NAME} PUBLIC psemek) + target_link_libraries(${TARGET_NAME} PUBLIC psemek ZLIB::ZLIB) endif() endforeach() diff --git a/examples/srtm.cpp b/examples/srtm.cpp index c3f81adf..2da413ee 100644 --- a/examples/srtm.cpp +++ b/examples/srtm.cpp @@ -35,6 +35,9 @@ #include #include #include +#include + +#include // TODO: use LRU cache for tile generation requests, combine with threadpool // TODO: fix frustum culling @@ -97,14 +100,10 @@ struct height_provider float height_provider::height_at(geom::vector const & v) { -// static std::string const data_path = "/home/lisyarus/data/srtm/dem/"; - static std::string const data_path = "/home/lisyarus/data/srtm/test/"; + static std::filesystem::path const data_path = "/home/lisyarus/data/srtm/dem"; float const lat = geom::deg(std::asin(v[2])); -// if (std::abs(lat) > 60.f) return 0.f; -// return -10.f; - float const lon = geom::deg(std::atan2(v[0], -v[1])); int ilat = std::floor(lat); @@ -130,27 +129,34 @@ float height_provider::height_at(geom::vector const & v) { { std::lock_guard lock{datums_mutex}; - if (datums.size() > 40) + if (datums.size() > 100) datums.clear(); } std::ostringstream os; -// os << "ASTGTMV003_"; if (ilat >= 0) - os << 'N' << std::setw(2) << std::setfill('0') << ilat; + os << "N/" << std::setw(2) << std::setfill('0') << ilat << '/'; else - os << 'S' << std::setw(2) << std::setfill('0') << (-ilat); + os << "S/" << std::setw(2) << std::setfill('0') << (-ilat) << '/'; if (ilon >= 0) - os << 'E' << std::setw(3) << std::setfill('0') << ilon; + os << "E/" << std::setw(3) << std::setfill('0') << ilon << '/'; else - os << 'W' << std::setw(3) << std::setfill('0') << (-ilon); + os << "W/" << std::setw(3) << std::setfill('0') << (-ilon) << '/'; - os << ".gray"; -// os << ".zip"; + os << "data.zip"; + + std::filesystem::path const filename = data_path / os.str(); + + if (!std::filesystem::exists(filename) || !std::filesystem::is_regular_file(filename)) + { + std::lock_guard lock{no_datums_mutex}; + no_datums.insert(id); + return 0.f; + } + + std::size_t zsize = std::filesystem::file_size(filename); - std::string const filename = data_path + os.str(); std::ifstream ifs(filename, std::ios::binary); - if (!ifs) { std::lock_guard lock{no_datums_mutex}; @@ -158,10 +164,26 @@ float height_provider::height_at(geom::vector const & v) return 0.f; } -// return 10.f; + std::vector zdata(zsize); + ifs.read(zdata.data(), zdata.size()); + ifs.close(); std::unique_ptr data{new std::uint16_t[3601 * 3601]}; - ifs.read(reinterpret_cast(data.get()), 3601 * 3601 * 2); + + z_stream infstream; + infstream.zalloc = Z_NULL; + infstream.zfree = Z_NULL; + infstream.opaque = Z_NULL; + // setup "b" as the input and "c" as the compressed output + infstream.avail_in = zdata.size(); // size of input + infstream.next_in = (Bytef *)zdata.data(); // input char array + infstream.avail_out = 3601 * 3601 * 2; // size of output + infstream.next_out = (Bytef *)data.get(); // output char array + + // the actual DE-compression work. + inflateInit(&infstream); + inflate(&infstream, Z_NO_FLUSH); + inflateEnd(&infstream); { std::lock_guard lock{datums_mutex}; @@ -196,6 +218,40 @@ static constexpr int node_child_size = 1 << node_child_depth; static constexpr int node_child_count = node_child_size * node_child_size; static constexpr int max_child_level = 20 - node_size_log2 - node_child_depth; +struct node_cache +{ + static std::filesystem::path data_path() { return "/home/lisyarus/data/srtm/cache"; } + + std::optional> load(std::size_t uid); + void store(std::size_t uid, std::vector const & data); +}; + +std::optional> node_cache::load(std::size_t uid) +{ + std::ostringstream oss; + oss << std::hex << uid; + + std::ifstream file(data_path() / oss.str(), std::ios::binary); + if (!file) + return std::nullopt; + + std::vector data(((node_size + 2) * (node_size + 1)) / 2); + file.read(reinterpret_cast(data.data()), data.size() * sizeof(data[0])); + return data; +} + +void node_cache::store(std::size_t uid, std::vector const & data) +{ + std::ostringstream oss; + oss << std::hex << uid; + + std::ofstream file(data_path() / oss.str(), std::ios::binary); + if (!file) + return; + + file.write(reinterpret_cast(data.data()), data.size() * sizeof(data[0])); +} + struct node { geom::vector v[3]; @@ -208,6 +264,7 @@ struct node struct node_controller { node_controller(); + ~node_controller(); node * root(int f); @@ -215,6 +272,8 @@ struct node_controller std::size_t loader_queue_size() const { return loader_.queue_size(); } + void preload(int max_level); + private: struct node_impl : node @@ -223,6 +282,7 @@ private: gfx::array array; gfx::buffer height_buffer; std::unique_ptr children[node_child_count]; + std::size_t uid; std::vector height_data; std::atomic height_data_ready = false; @@ -242,8 +302,10 @@ private: std::unique_ptr roots_[20]; height_provider height_provider_; + node_cache node_cache_; - util::threadpool loader_{"load", 4}; + util::threadpool loader_{"load", 1}; + std::atomic cancel_ = false; std::size_t node_count_ = 0; @@ -282,11 +344,17 @@ node_controller::node_controller() index_buffer_.load(indices, gl::STATIC_DRAW); } +node_controller::~node_controller() +{ + cancel_ = true; +} + node * node_controller::root(int f) { if (!roots_[f]) { auto n = make_node(); + n->uid = (1 << 5) | f; auto face = cg::faces(icosahedron_)[f]; n->v[0] = icosahedron_.vertices[face[0]] - geom::point::zero(); @@ -299,6 +367,40 @@ node * node_controller::root(int f) return roots_[f].get(); } +void node_controller::preload(int) +{ + std::string strid; + auto visit = util::recursive([this, &strid](auto & self, node * n, int level) -> void + { + auto nn = static_cast(n); + log::info() << "Loading " << strid << "..."; + util::clock<> clock; + nn->load_heights(); + while (!nn->height_data_ready) + std::this_thread::yield(); + auto const time = clock.count(); + log::info() << "Loading " << strid << " done (" << time << "s)"; + + if (level + 1 < max_child_level && (time >= 0.05 || level < 5)) + { + std::string old_strid = strid; + for (int i = 0; i < node_child_count; ++i) + { + strid = old_strid + "/" + std::to_string(i); + self(n->child(i), level + 1); + } + + strid = old_strid; + } + }); + + for (int f = 0; f < 20; ++f) + { + strid = std::to_string(f); + visit(root(f), 0); + } +} + std::unique_ptr node_controller::make_node() { auto n = std::make_unique(); @@ -321,7 +423,6 @@ bool node_controller::node_impl::draw(int level) { if (!height_data_loader_dispatched) { - height_data_loader_dispatched = true; load_heights(); } @@ -348,6 +449,7 @@ node * node_controller::node_impl::child(int id) if (!children[id]) { auto n = controller->make_node(); + n->uid = (uid << (2 * node_child_depth)) | id; int i0, j0, i1, j1, i2, j2; @@ -399,7 +501,18 @@ node * node_controller::node_impl::child(int id) void node_controller::node_impl::load_heights() { + height_data_loader_dispatched = true; controller->loader_.dispatch([this]{ + if (controller->cancel_) return; + + auto cached = controller->node_cache_.load(uid); + if (cached) + { + height_data = std::move(*cached); + height_data_ready = true; + return; + } + height_data.assign(((node_size + 2) * (node_size + 1)) / 2, 0); auto * out = height_data.data(); @@ -417,10 +530,14 @@ void node_controller::node_impl::load_heights() { for (int j = 0; j <= i; ++j) { + if (controller->cancel_) return; + *out++ = static_cast(controller->height_provider_.height_at(at(i, j))); } } + controller->node_cache_.store(uid, height_data); + height_data_ready = true; }); } @@ -498,8 +615,8 @@ out vec4 out_color; void main() { - float l = (0.5 + dot(normalize(g_normal), u_light) * 0.5); - out_color = vec4(g_color * l, 1.0); + float l = max(0.2, dot(normalize(g_normal), u_light)); + out_color = vec4(pow(g_color * l, vec3(1.0 / 2.2)), 1.0); })"; static char const tile_far_fs[] = @@ -514,8 +631,8 @@ out vec4 out_color; void main() { vec3 normal = cross(dFdx(pos), dFdy(pos)); - float l = (0.5 + dot(normalize(normal), u_light) * 0.5); - out_color = vec4(color * l, 1.0); + float l = max(0.2, dot(normalize(normal), u_light)); + out_color = vec4(pow(color * l, vec3(1.0 / 2.2)), 1.0); })"; struct srtm_app @@ -567,13 +684,26 @@ srtm_app::srtm_app() selected_mesh.setup>(); { - util::array colors({5}); + util::array colors({16}); - colors(0) = {0, 127, 0}; - colors(1) = {127, 127, 0}; - colors(2) = {127, 63, 0}; - colors(3) = {127, 0, 0}; - colors(4) = {191, 191, 191}; + auto * c = colors.data(); + + *c++ = {0, 63, 0}; + *c++ = {0, 127, 0}; + *c++ = {63, 127, 0}; + *c++ = {127, 127, 0}; + *c++ = {95, 95, 0}; + *c++ = {63, 63, 0}; + *c++ = {95, 63, 0}; + *c++ = {127, 95, 0}; + *c++ = {127, 63, 0}; + *c++ = {127, 31, 0}; + *c++ = {127, 0, 0}; + *c++ = {95, 0, 0}; + *c++ = {63, 0, 0}; + *c++ = {191, 191, 191}; + *c++ = {159, 159, 191}; + *c++ = {127, 127, 191}; color_map.load(colors); color_map.clamp(); color_map.linear_filter(); @@ -591,6 +721,8 @@ srtm_app::srtm_app() color_map_neg.clamp(); color_map_neg.linear_filter(); } + +// nodes.preload(6); } void srtm_app::on_resize(int width, int height) @@ -615,12 +747,12 @@ void srtm_app::update() if (is_key_down(SDLK_q)) { - camera.rotateXY(-0.04f); + camera.rotateXY(- 4.f * dt); } if (is_key_down(SDLK_e)) { - camera.rotateXY(0.04f); + camera.rotateXY(4.f * dt); } float const camera_speed = std::min(5.f, geom::distance(camera.pos, geom::point::zero()) - 1.f); @@ -693,7 +825,8 @@ void srtm_app::present() std::vector info; - gl::ClearColor(0.9f, 0.9f, 0.9f, 0.f); +// gl::ClearColor(0.9f, 0.9f, 0.9f, 0.f); + gl::ClearColor(0.0f, 0.0f, 0.0f, 0.f); gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); gl::LineWidth(2.f); @@ -721,17 +854,19 @@ void srtm_app::present() auto const frustum = cg::frustum(camera_transform); (void)frustum; + auto light = geom::normalized(geom::vector{camera_pos[0]-camera_pos[1], camera_pos[1]+camera_pos[0], 0.f}); + tile_close_program.bind(); tile_close_program["u_transform"] = camera_transform; tile_close_program["u_N"] = static_cast(node_size); - tile_close_program["u_light"] = geom::vector{0.f, 0.f, 1.f}; + tile_close_program["u_light"] = light; tile_close_program["u_colormap"] = 0; tile_close_program["u_colormap_neg"] = 1; tile_close_program["u_far"] = camera.far_clip; tile_far_program.bind(); tile_far_program["u_transform"] = camera_transform; tile_far_program["u_N"] = static_cast(node_size); - tile_far_program["u_light"] = geom::vector{0.f, 0.f, 1.f}; + tile_far_program["u_light"] = light; tile_far_program["u_colormap"] = 0; tile_far_program["u_colormap_neg"] = 1; tile_far_program["u_far"] = camera.far_clip; @@ -798,12 +933,15 @@ void srtm_app::present() auto c = camera_pos - o; if (geom::det(v[0], v[1], c) >= 0.f && geom::det(v[1], v[2], c) >= 0.f && geom::det(v[2], v[0], c) >= 0.f) distance = std::min(distance, geom::length(c) - 1.f); - if (auto d = edge(v[0], v[1], c); d) - distance = std::min(distance, *d); - if (auto d = edge(v[1], v[2], c); d) - distance = std::min(distance, *d); - if (auto d = edge(v[2], v[0], c); d) - distance = std::min(distance, *d); + else + { + if (auto d = edge(v[0], v[1], c); d) + distance = std::min(distance, *d); + if (auto d = edge(v[1], v[2], c); d) + distance = std::min(distance, *d); + if (auto d = edge(v[2], v[0], c); d) + distance = std::min(distance, *d); + } distance = std::min(distance, geom::length(c - v[0])); distance = std::min(distance, geom::length(c - v[1])); distance = std::min(distance, geom::length(c - v[2])); @@ -897,7 +1035,7 @@ void srtm_app::present() opts.x = gfx::painter::x_align::left; opts.y = gfx::painter::y_align::top; opts.f = gfx::painter::font::font_9x12; - opts.c = gfx::cyan; + opts.c = gfx::gray; opts.scale = 2.f; for (int l = 0; l < info.size(); ++l) @@ -905,7 +1043,7 @@ void srtm_app::present() painter.text({10.f + 1.f, 10.f + 24.f * l + 1.f}, info[l], opts); } - opts.c = gfx::black; + opts.c = gfx::white; for (int l = 0; l < info.size(); ++l) {