diff --git a/examples/srtm.cpp b/examples/srtm.cpp index 55a626db..d5cec440 100644 --- a/examples/srtm.cpp +++ b/examples/srtm.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -25,6 +26,13 @@ #include #include #include +#include + +#include +#include +#include +#include +#include using namespace psemek; @@ -59,141 +67,147 @@ private: T speed_; }; -static char const tile_vs[] = -R"(#version 330 - -uniform mat4 u_transform; - -uniform int u_icosa_face; -uniform int u_N; -uniform vec3 u_p0; -uniform vec3 u_p1; -uniform vec3 u_p2; -uniform vec4 u_color; - -out vec3 color; - -void main() +struct height_provider { - int i = int(floor(0.5 * (sqrt(1.0 + 8.0 * gl_VertexID) - 1.0))); - int j = gl_VertexID - (i * (i + 1)) / 2; + float height_at(geom::vector const & v); - float t0 = 1.0 - float(i) / float(u_N); - float t1 = float(j) / float(u_N); - float t2 = 1.0 - t0 - t1; - - vec3 p = normalize(u_p0 * t0 + u_p1 * t1 + u_p2 * t2); - gl_Position = u_transform * vec4(p, 1.0); - color = u_color.rgb; -})"; - -static char const tile_fs[] = -R"(#version 330 - -in vec3 color; -out vec4 out_color; - -void main() -{ - out_color = vec4(color, 1.0); -})"; - -static char const test_vs[] = -R"(#version 330 - -uniform mat4 u_transform; - -layout (location = 0) in vec4 in_position; - -out vec4 color; - -void main() -{ - gl_Position = u_transform * in_position; - color = vec4(in_position.xyz * 0.5 + vec3(0.5), 1.0); -})"; - -static char const test_fs[] = -R"(#version 330 - -in vec4 color; -out vec4 out_color; - -void main() -{ - out_color = color; -})"; - -struct srtm_app - : app::app -{ - srtm_app(); - - void on_resize(int width, int height) override; - - void on_mouse_move(int x, int y, int dx, int dy) override; - - void update() override; - void present() override; - -// geom::spherical_camera camera; - geom::free_camera camera; - smooth_updater camera_azimuthal_angle_updater{camera.azimuthal_angle, 20.f}; - smooth_updater camera_elevation_angle_updater{camera.elevation_angle, 20.f}; - bool camera_forward = false; - - static constexpr int tile_size_log2 = 8; - static constexpr int tile_size = (1 << tile_size_log2); - - gfx::mesh tile_mesh[tile_size_log2 + 1]; - - gfx::program tile_program{tile_vs, tile_fs}; - gfx::program test_program{test_vs, test_fs}; - - gfx::mesh test_mesh; - - struct test_object + struct datum_id_hash { - cg::box body; - gfx::mesh mesh; - int x, y; + std::size_t operator() (std::pair const & p) const + { + return ((p.first + 180) << 16) | (p.second + 90); + } }; - int const test_object_count = 21; - std::vector test_objects; - - util::clock> frame_clock; - util::moving_average frame_dt_average{32}; - - gfx::painter painter; + std::unordered_map, std::unique_ptr, datum_id_hash> datums; + std::unordered_set, datum_id_hash> no_datums; }; -srtm_app::srtm_app() - : app("SRTM", 4) +static geom::interval height_range; + +float height_provider::height_at(geom::vector const & v) { - vsync(false); - show_cursor(false); + static std::string const data_path = "/home/lisyarus/data/srtm/dem-unpacked/"; - camera.fov_y = geom::rad(45.f); - camera.near_clip = 0.0001f; - camera.far_clip = 10.f; -// camera.target = {0.f, 0.f, 0.f}; - camera.pos = {0.f, -10.f, 0.f}; - camera.azimuthal_angle = 0.f; - camera.elevation_angle = 0.f; -// camera.distance = 5.f; + float const lat = geom::deg(std::asin(v[2])); - camera_azimuthal_angle_updater = camera.azimuthal_angle; - camera_elevation_angle_updater = camera.elevation_angle; -// camera_distance_updater = camera.distance; + if (std::abs(lat) > 60.f) return 0.f; + return -10.f; - for (std::size_t N = 0; N <= tile_size_log2; ++N) + float const lon = geom::deg(std::atan2(v[0], -v[1])); + + int ilat = std::floor(lat); + int ilon = std::floor(lon); + + auto id = std::make_pair(ilat, ilon); + + if (no_datums.count(id) > 0) + return -10.f; + + if (datums.count(id) == 0) { - std::vector indices; + if (datums.size() > 40) + datums.clear(); - std::size_t step = tile_size / (1 << N); + std::ostringstream os; + if (ilat >= 0) + os << 'N' << std::setw(2) << std::setfill('0') << ilat; + else + os << 'S' << std::setw(2) << std::setfill('0') << (-ilat); + if (ilon >= 0) + os << 'E' << std::setw(3) << std::setfill('0') << ilon; + else + os << 'W' << std::setw(3) << std::setfill('0') << (-ilon); - auto idx = [step](std::size_t i, std::size_t j) -> std::uint16_t + os << ".hgt"; + + std::string const filename = data_path + os.str(); + std::ifstream ifs(filename, std::ios::binary); + + if (!ifs) + { + no_datums.insert(id); + return -10.f; + } + + return 10.f; + + std::unique_ptr values{new std::int16_t[3601 * 3601]}; + ifs.read(reinterpret_cast(values.get()), 3601 * 3601 * 2); + datums[id] = std::move(values); + } + + auto const * values = datums[id].get(); + + int const tlat = geom::clamp(std::round((lat - ilat) * 3600), {0, 3600}); + int const tlon = geom::clamp(std::round((lon - ilon) * 3600), {0, 3600}); + + auto h = values[tlat * 3601 + 3600 - tlon]; + h = ((h & 255) << 8) | (h >> 8); + height_range |= (int)h; + return h; +} + +struct node +{ + geom::vector v[3]; + + virtual void draw(int level) = 0; + virtual node * child(int id) = 0; +}; + +struct node_controller +{ + node_controller(); + + node * root(int f); + + std::size_t node_count() const { return node_count_; } + +private: + struct node_impl + : node + { + node_controller * controller; + gfx::array array; + gfx::buffer height_buffer; + std::unique_ptr children[256]; + + std::vector height_data; + std::atomic height_data_ready = false; + bool height_data_loaded = false; + + void draw(int level) override; + node * child(int id) override; + + void load_heights(); + }; + + cg::icosahedron icosahedron_; + + gfx::buffer index_buffer_; + std::size_t index_counts_[10]; + std::unique_ptr roots_[20]; + + height_provider height_provider_; + + util::threadpool loader_{"load", 1}; + + std::size_t node_count_ = 0; + + std::unique_ptr make_node(); +}; + +node_controller::node_controller() + : icosahedron_{geom::point::zero(), 1.f} +{ + std::vector indices; + index_counts_[0] = 0; + for (std::size_t N = 0; N <= 8; ++N) + { + std::size_t step = 256 >> N; + + auto idx = [step](std::size_t i, std::size_t j) -> std::uint32_t { i *= step; j *= step; @@ -208,36 +222,229 @@ srtm_app::srtm_app() indices.push_back(idx(i, j)); } indices.push_back(idx(i + 1, i + 1)); - indices.push_back(0xffffu); + indices.push_back(0xffffffffu); } - tile_mesh[N].load_index(indices, gl::TRIANGLE_STRIP, gl::STATIC_DRAW); + index_counts_[N + 1] = indices.size(); } + index_buffer_.load(indices, gl::STATIC_DRAW); +} - for (int x = - test_object_count + 1; x <= test_object_count - 1; x += 2) +node * node_controller::root(int f) +{ + if (!roots_[f]) { - for (int y = - test_object_count + 1; y <= test_object_count - 1; y += 2) - { - auto & o = test_objects.emplace_back(); + auto n = make_node(); - o.x = (x + test_object_count - 1) / 2; - o.y = (y + test_object_count - 1) / 2; + auto face = cg::faces(icosahedron_)[f]; + n->v[0] = icosahedron_.vertices[face[0]] - geom::point::zero(); + n->v[1] = icosahedron_.vertices[face[1]] - geom::point::zero(); + n->v[2] = icosahedron_.vertices[face[2]] - geom::point::zero(); - geom::box b {{ - {x - 1.f, x + 1.f}, - {y - 1.f, y + 1.f}, - {0 - 1.f, 0 + 1.f} - }}; + n->load_heights(); - o.body = cg::box(b); - - auto const & vertices = cg::vertices(o.body); - auto const & edges = cg::edges(o.body); - - o.mesh.setup>(); - o.mesh.load(vertices.data(), vertices.size(), edges.data(), edges.size()); - } + roots_[f] = std::move(n); } + + return roots_[f].get(); +} + +std::unique_ptr node_controller::make_node() +{ + auto n = std::make_unique(); + + n->controller = this; + n->array.bind(); + n->height_buffer.bind(); + gl::EnableVertexAttribArray(0); + gl::VertexAttribPointer(0, 1, gl::SHORT, gl::FALSE, 0, nullptr); + gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer_.id()); + + ++node_count_; + + return n; +} + +void node_controller::node_impl::draw(int level) +{ + if (!height_data_loaded) + { + if (!height_data_ready) return; + + height_buffer.load(height_data, gl::STATIC_DRAW); + height_data.clear(); + height_data_loaded = true; + } + + array.bind(); + std::size_t offset = controller->index_counts_[level]; + std::size_t count = controller->index_counts_[level + 1] - offset; + gl::DrawElements(gl::TRIANGLE_STRIP, count, gl::UNSIGNED_INT, (std::uint32_t const *)(nullptr) + offset); +} + +node * node_controller::node_impl::child(int id) +{ + if (!children[id]) + { + auto n = controller->make_node(); + + int i0, j0, i1, j1, i2, j2; + + if (id < 136) + { + int i = int(std::floor(0.5f * (sqrt(1.f + 8.f * id) - 1.f))); + int j = id - (i * (i + 1)) / 2; + + i0 = i; + j0 = j; + i1 = i + 1; + j1 = j + 1; + i2 = i + 1; + j2 = j; + } + else + { + int i = int(std::floor(0.5f * (sqrt(1.f + 8.f * (id - 136)) - 1.f))); + int j = id - 136 - (i * (i + 1)) / 2; + + i0 = i + 1; + j0 = j; + i1 = i + 1; + j1 = j + 1; + i2 = i + 2; + j2 = j + 1; + } + + auto at = [this](int i, int j) + { + float t0 = 1.f - (1.f * i) / 16.f; + float t1 = (1.f * j) / 16.f; + float t2 = 1.f - t0 - t1; + + return geom::normalized(v[0] * t0 + v[1] * t1 + v[2] * t2); + }; + + n->v[0] = at(i0, j0); + n->v[1] = at(i1, j1); + n->v[2] = at(i2, j2); + + n->load_heights(); + + children[id] = std::move(n); + } + + return children[id].get(); +} + +void node_controller::node_impl::load_heights() +{ + controller->loader_.dispatch([this]{ + height_data.assign((257 * 258) / 2, 0); + + auto * out = height_data.data(); + + auto at = [this](int i, int j) + { + float t0 = 1.f - (1.f * i) / 256.f; + float t1 = (1.f * j) / 256.f; + float t2 = 1.f - t0 - t1; + + return geom::normalized(v[0] * t0 + v[1] * t1 + v[2] * t2); + }; + + for (int i = 0; i <= 256; ++i) + { + for (int j = 0; j <= i; ++j) + { + *out++ = static_cast(controller->height_provider_.height_at(at(i, j))); + } + } + + height_data_ready = true; + }); +} + +static char const tile_vs[] = +R"(#version 330 + +uniform mat4 u_transform; + +uniform int u_N; +uniform vec3 u_p0; +uniform vec3 u_p1; +uniform vec3 u_p2; + +layout (location = 0) in float in_height; + +out vec3 color; + +void main() +{ + int i = int(floor(0.5 * (sqrt(1.0 + 8.0 * gl_VertexID) - 1.0))); + int j = gl_VertexID - (i * (i + 1)) / 2; + + float t0 = 1.0 - float(i) / float(u_N); + float t1 = float(j) / float(u_N); + float t2 = 1.0 - t0 - t1; + + vec3 p = normalize(u_p0 * t0 + u_p1 * t1 + u_p2 * t2) * (1.0 + in_height / 6400000.0); + gl_Position = u_transform * vec4(p, 1.0); + color = (in_height < 0.0) ? vec3(0.0, 0.0, 0.5) : vec3(0.0, 0.5, 0.0); +})"; + +static char const tile_fs[] = +R"(#version 330 + +in vec3 color; +out vec4 out_color; + +void main() +{ + out_color = vec4(color, 1.0); +})"; + +struct srtm_app + : app::app +{ + srtm_app(); + + void on_resize(int width, int height) override; + + void on_mouse_move(int x, int y, int dx, int dy) override; + + void update() override; + void present() override; + + geom::free_camera camera; + smooth_updater camera_azimuthal_angle_updater{camera.azimuthal_angle, 20.f}; + smooth_updater camera_elevation_angle_updater{camera.elevation_angle, 20.f}; + bool camera_forward = false; + + node_controller nodes; + + gfx::program tile_program{tile_vs, tile_fs}; + + util::clock> frame_clock; + util::moving_average frame_dt_average{32}; + + gfx::painter painter; +}; + +srtm_app::srtm_app() + : app("SRTM", 4) +{ + vsync(true); + show_cursor(false); + + camera.fov_y = geom::rad(45.f); + camera.near_clip = 0.0001f; + camera.far_clip = 10.f; + camera.pos = {0.f, -10.f, 0.f}; + camera.azimuthal_angle = 0.f; + camera.elevation_angle = 0.f; + + camera_azimuthal_angle_updater = camera.azimuthal_angle; + camera_elevation_angle_updater = camera.elevation_angle; } void srtm_app::on_resize(int width, int height) @@ -342,14 +549,14 @@ void srtm_app::present() gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); gl::LineWidth(2.f); - gl::PolygonMode(gl::FRONT_AND_BACK, gl::LINE); + gl::PolygonMode(gl::FRONT_AND_BACK, gl::FILL); gl::PointSize(5.f); gl::Enable(gl::CULL_FACE); gl::Enable(gl::DEPTH_TEST); gl::DepthFunc(gl::LEQUAL); gl::Enable(gl::PRIMITIVE_RESTART); - gl::PrimitiveRestartIndex(0xffffu); + gl::PrimitiveRestartIndex(0xffffffffu); { auto d = geom::distance(camera.pos, geom::point::zero()); @@ -359,22 +566,24 @@ void srtm_app::present() auto const camera_transform = camera.transform(); auto const camera_pos = camera.position(); - auto const camera_direction = camera.direction(); +// auto const camera_direction = camera.direction(); info.push_back(util::to_string("Camera height: ", (geom::distance(camera_pos, geom::point::zero()) - 1.f) * 6400000.f, " m")); auto const frustum = cg::frustum(camera_transform); + (void)frustum; tile_program.bind(); tile_program["u_transform"] = camera_transform; - tile_program["u_N"] = static_cast(tile_size); + tile_program["u_N"] = static_cast(256); info.push_back(util::to_string("Camera pos: ", camera_pos)); std::size_t rendered_tiles = 0; std::vector id; - auto visit = util::recursive([&](auto & self, geom::vector const (&v)[3], int level = 0) -> void + auto visit = util::recursive([&](auto & self, node * n, int level = 0) -> void { + auto const & v = n->v; auto const o = geom::point::zero(); auto m = (v[0] + v[1] + v[2]) / 3.f; m = geom::normalized(m) * (1.f + 1.f / 6400.f) - m; @@ -400,7 +609,7 @@ void srtm_app::present() if (cg::separation(body, frustum).second > 0.f) return; - bool const selected = geom::intersect(geom::ray{camera_pos, camera_direction}, t); +// bool const selected = false && geom::intersect(geom::ray{camera_pos, camera_direction}, t); float on_screen_unit; @@ -433,51 +642,20 @@ void srtm_app::present() assert(on_screen_unit > 0.f); - float const max_triangle_size = 10.f; // pixels + float const max_triangle_size = 5.f; // pixels float const side_length = icosa_side / (1 << (level * 4)); int tile_n = std::ceil(std::log2(on_screen_unit * side_length / max_triangle_size)); - if (level < 3 && tile_n > tile_size_log2) + if (level < 3 && tile_n > 8) { - std::size_t const C = 16; - - auto at = [&](std::size_t i, std::size_t j) - { - float t0 = 1.f - (1.f * i) / C; - float t1 = (1.f * j) / C; - float t2 = 1.f - t0 - t1; - - return geom::normalized(v[0] * t0 + v[1] * t1 + v[2] * t2); - }; - - std::size_t child_id = 0; - - auto child = [&](std::size_t i0, std::size_t j0, std::size_t i1, std::size_t j1, std::size_t i2, std::size_t j2) - { - geom::vector v[3]; - v[0] = at(i0, j0); - v[1] = at(i1, j1); - v[2] = at(i2, j2); - id.push_back(child_id++); - self(v, level + 1); - id.pop_back(); - }; - - for (std::size_t i = 0; i < C; ++i) - { - for (std::size_t j = 0; j < i; ++j) - { - child(i + 1, j, i, j, i + 1, j + 1); - child(i + 1, j + 1, i, j, i, j + 1); - } - child(i + 1, i, i, i, i + 1, i + 1); - } + for (int id = 0; id < 256; ++id) + self(n->child(id), level + 1); } else { - tile_n = geom::clamp(tile_n, {0, tile_size_log2}); + tile_n = geom::clamp(tile_n, {0, 8}); ++rendered_tiles; @@ -494,74 +672,25 @@ void srtm_app::present() tile_program["u_p2"] = v[2]; tile_program["u_color"] = colors[tile_n % 4]; - tile_mesh[tile_n].draw(); - - if (selected) - { - gl::Disable(gl::DEPTH_TEST); - tile_program["u_color"] = gfx::white.as_color_4f(); - tile_mesh[0].draw(); - gl::Enable(gl::DEPTH_TEST); - - info.push_back(util::to_string("Selected id: ", id)); - info.push_back(util::to_string("Selected separation: ", cg::separation(body, frustum).second)); - } + n->draw(tile_n); } }); -// if(false) -// for (int f = 4; f < 5; ++f) + gfx::check_error(); + for (int f = 0; f < 20; ++f) { - geom::vector v[3]; - v[0] = icosa_vertices[icosa_faces[f][0]] - geom::point::zero(); - v[1] = icosa_vertices[icosa_faces[f][1]] - geom::point::zero(); - v[2] = icosa_vertices[icosa_faces[f][2]] - geom::point::zero(); - id.push_back(f); - visit(v); - id.pop_back(); + visit(nodes.root(f)); } info.push_back(util::to_string("Tiles: ", rendered_tiles)); - - if (false) - { - gl::PolygonMode(gl::FRONT_AND_BACK, gl::FILL); - gl::Disable(gl::CULL_FACE); - - test_program.bind(); - test_program["u_transform"] = camera_transform; - int visible_count = 0; - for (auto const & o : test_objects) - { - gfx::color_rgba color; - - if (cg::separation(o.body, frustum).second < 0.f) - { - ++visible_count; - o.mesh.draw(); - - color = gfx::red; - } - else - { - color = gfx::black; - } - - float sx = width() - 10 * test_object_count + o.x * 10; - float sy = height() - 10 * test_object_count + (test_object_count - 1 - o.y) * 10; - - painter.rect({{{sx, sx + 10.f}, {sy, sy + 10.f}}}, color); - } - - info.push_back(util::to_string("Visible count: ", visible_count)); - } - { float s = 10.f; painter.line({width() / 2.f - s, height() / 2.f}, {width() / 2.f + s, height() / 2.f}, 3.f, gfx::cyan, false); painter.line({width() / 2.f, height() / 2.f - s}, {width() / 2.f, height() / 2.f + s}, 3.f, gfx::cyan, false); } + info.push_back(util::to_string("Heights: ", height_range)); + info.push_back(util::to_string("Nodes: ", nodes.node_count())); { info.insert(info.begin(), util::to_string("FPS: ", 1.f / frame_dt_average.average()));