From a5682fd50dec18f07558c3d19e53a9aed6103657 Mon Sep 17 00:00:00 2001 From: Aaron <aaron.kreuzberg@rwth-aachen.de> Date: Wed, 3 Nov 2021 16:39:44 +0100 Subject: [PATCH] picking samples --- extern/glow-extras | 2 +- samples/basic/viewer/main.cc | 1146 ++++++++++++++++------------------ 2 files changed, 539 insertions(+), 609 deletions(-) diff --git a/extern/glow-extras b/extern/glow-extras index 1978c6b..a9e9479 160000 --- a/extern/glow-extras +++ b/extern/glow-extras @@ -1 +1 @@ -Subproject commit 1978c6b5209d2de5fbfbb250f74385817149b193 +Subproject commit a9e947979d85b8186c9faddb13d442dc261ab322 diff --git a/samples/basic/viewer/main.cc b/samples/basic/viewer/main.cc index 55e8944..c5e50e4 100644 --- a/samples/basic/viewer/main.cc +++ b/samples/basic/viewer/main.cc @@ -27,334 +27,6 @@ // path to sample files std::string const dataPath = glow::util::pathOf(__FILE__) + "/../../../data/"; -void simple_picking(pm::Mesh& m, pm::vertex_attribute<tg::pos3>& pos, pm::face_attribute<tg::color3> const& col) -{ - // { - // // In this case a Picker is defined for the Renderable (Picking Texture filled) but no callback will be executed. - // gv::view(pos, col, gv::pick(), "Picker defined without callback"); - // } - - { - auto g = gv::grid(); - - // Only on_left_click callback defined - gv::view(pos, col, - gv::pick().onLeftClick( - [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) -> gv::picking_result - { - std::cout << "Something has been picked! ON_LEFT_CLICK" - << "ID: " << (int)face_id << std::endl; - std::cout << "World_Position " << world_pos << std::endl; - std::cout << "Normal " << normal << std::endl; - gv::picking_result res; - res.mPickingColor = tg::color3::cyan; - return res; - }), - "picking MeshRenderable - callback on left click"); - - // Only on_left_click callback defined - LineRenderable - uncolored - gv::view(gv::lines(pos), - gv::pick().onLeftClick( - [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) - { - std::cout << "Something has been picked! ON_LEFT_CLICK" - << "ID: " << int(face_id) << std::endl; - std::cout << "World_Position" << world_pos << std::endl; - std::cout << "Normal" << normal << std::endl; - }), - "picking LineRenderable - callback on left click"); - - { - // Only on_left_click callback defined - PointRenderable - uncolored - gv::view(gv::points(pos), - gv::pick().onLeftClick( - [&](pm::vertex_index vertex_id, tg::pos3 world_pos, tg::vec3 normal) - { - std::cout << "Something has been picked! ON_LEFT_CLICK" - << "ID: " << int(vertex_id) << std::endl; - std::cout << "World_Position" << world_pos << std::endl; - std::cout << "Normal" << normal << std::endl; - return; - }), - "picking PointRenderable - callback on left click - spheres"); - } - - { - // Only on_left_click callback defined - PointRenderable - uncolored - square billboards - NOT WORKING APPROPRIATELY - gv::view(gv::points(pos).point_size_world(0.03f).camera_facing().square(), - gv::pick().onLeftClick( - [&](pm::vertex_index vertex_id, tg::pos3 world_pos, tg::vec3 normal) - { - std::cout << "Something has been picked! ON_LEFT_CLICK" - << "ID: " << int(vertex_id) << std::endl; - std::cout << "World_Position" << world_pos << std::endl; - std::cout << "Normal" << normal << std::endl; - return; - }), - "picking PointRenderables - callback on left click - billboards"); - } - } - { - auto r1 = gv::make_and_configure_renderable(pos, col, - gv::pick().onRightClick( - [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) - { - std::cout << "Something has been picked! ON_RIGHT_CLICK" - << "ID: " << int(face_id) << std::endl; - return; - }), - "picking MeshRenderable - callback on right click"); - auto r2 = gv::make_and_configure_renderable(pos, col, - gv::pick().onHover( - [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) - { - std::cout << "Something has been picked! ON_HOVER" - << "ID: " << (int)face_id << std::endl; - return; - }), - "picking MeshRenderable - callback on hover"); - gv::interactive( - [&](float) - { - auto g = gv::grid(); - gv::view(r1); - gv::view(r2); - }); - } - { - auto g = gv::grid(); - - // Only on_right_click callback defined - gv::view(pos, col, - gv::pick().onRightClick( - [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) - { - std::cout << "Something has been picked! ON_RIGHT_CLICK" - << "ID: " << int(face_id) << std::endl; - return; - }), - "picking MeshRenderable - callback on right click"); - - // Only on_hover callback defined - gv::view(pos, col, - gv::pick().onHover( - [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) - { - std::cout << "Something has been picked! ON_HOVER" - << "ID: " << (int)face_id << std::endl; - return; - }), - "picking MeshRenderable - callback on hover"); - } - - { - /* Picking in interactive mode with ImGui example. Does only work for MeshRenderable in the given configuration because face_indices are not included in Point- or LineRenderables.*/ - pm::face_index face_index_i; - uint32_t face_id_i; - tg::pos3 world_pos_i; - tg::vec3 normal_i; - - pm::Mesh m2; - m2.copy_from(m); - - pm::vertex_attribute<tg::pos3> pos2(m2); - pos2.copy_from(pos); - - gv::interactive( - [&](auto) - { - gv::view(pos2, col, - gv::pick().onLeftClick( - [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) - { - std::cout << "Something has been picked! ON_LEFT_CLICK" - << "ID: " << int(face_id) << std::endl; - std::cout << "World_Position" << world_pos << std::endl; - std::cout << "Normal" << normal << std::endl; - - face_index_i = face_id; - world_pos_i = world_pos; - normal_i = normal; - }), - "picking interactive mode"); - - ImGui::Begin("Picking"); - - ImGui::Value("pm::face_index", face_index_i.value); - - ImGui::Value("WorldPos.x", world_pos_i.x); - ImGui::Value("WorldPos.y", world_pos_i.y); - ImGui::Value("WorldPos.z", world_pos_i.z); - - ImGui::Value("Normal.x", normal_i.x); - ImGui::Value("Normal.y", normal_i.y); - ImGui::Value("Normal.z", normal_i.z); - - if (ImGui::Button("Delete selected face")) - { - // delete selected face - after deleting one face another face has to be picked - std::cout << "DELETE FACE" << std::endl; - m2.faces().remove(m2.faces()[face_index_i]); - } - - ImGui::End(); - }); - } - - { - /* Picking in interactive mode with ImGui example. Does only work for MeshRenderable in the given configuration because face_indices are not included in Point- or LineRenderables.*/ - pm::vertex_index vertex_index_i; - uint32_t vertex_id_i; - tg::pos3 world_pos_i; - tg::vec3 normal_i; - - pm::Mesh m2; - m2.copy_from(m); - - pm::vertex_attribute<tg::pos3> pos2(m2); - pos2.copy_from(pos); - - // auto copy = pm::copy(pos.mesh(), pos); - // auto m2 = copy._Myfirst._Val.get(); - // auto pos2 = copy._Get_rest()._Myfirst._Val; - - gv::interactive( - [&](auto) - { - gv::view(gv::points(pos2), - gv::pick().onLeftClick( - [&vertex_index_i, &world_pos_i, &normal_i](pm::vertex_index vertex_id, tg::pos3 world_pos, tg::vec3 normal) - { - std::cout << "Something has been picked! ON_LEFT_CLICK" - << "ID: " << int(vertex_id) << std::endl; - std::cout << "World_Position" << world_pos << std::endl; - std::cout << "Normal" << normal << std::endl; - - vertex_index_i = vertex_id; - world_pos_i = world_pos; - normal_i = normal; - - return; - }), - "picking interactive mode - PointRenderable"); - - ImGui::Begin("Picking"); - - ImGui::Value("pm::vertex_index", vertex_index_i.value); - - ImGui::Value("WorldPos.x", world_pos_i.x); - ImGui::Value("WorldPos.y", world_pos_i.y); - ImGui::Value("WorldPos.z", world_pos_i.z); - - ImGui::Value("Normal.x", normal_i.x); - ImGui::Value("Normal.y", normal_i.y); - ImGui::Value("Normal.z", normal_i.z); - - if (ImGui::Button("Delete selected vertex")) - { - // delete selected vertex - after deleting one vertex another vertex has to be picked - std::cout << "DELETE VERTEX" << std::endl; - m2.vertices().remove(m2.vertices()[vertex_index_i]); - } - - ImGui::End(); - }); - } - - { - // On_hover, on_right_click, and on_left_click callbacks defined simultaneously - gv::view(pos, col, - gv::pick() - .onLeftClick( - [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) - { - std::cout << "Something has been picked! ON_LEFT_CLICK" - << "ID: " << int(face_id) << std::endl; - return; - }) - .onRightClick( - [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) - { - std::cout << "Something has been picked! ON_RIGHT_CLICK" - << "ID: " << int(face_id) << std::endl; - return; - }) - .onHover( - [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) - { - std::cout << "Something has been picked! ON_HOVER" - << "ID: " << int(face_id) << std::endl; - return; - }), - "picking MeshRenderable - multiple callbacks: on left click, on right click, on hover" - - ); - } - - { - // User-defined Picking IDs - MeshRenderable - pm::face_attribute<int32_t> fa = pm::face_attribute<int32_t>(m); - int i = 0; - for (auto f : m.faces()) - { - fa[f] = 1000 + i; - i++; - } - gv::view(pos, col, - gv::pick(fa).onLeftClick( - [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) -> gv::picking_result - { - std::cout << "Something has been picked! ON_LEFT_CLICK" - << "ID: " << (int)face_id << std::endl; - std::cout << "World_Position" << world_pos << std::endl; - std::cout << "Normal" << normal << std::endl; - gv::picking_result res; - res.mPickingColor = tg::color3::cyan; - return res; - }), - "simple picking: callback on left click - user-defined IDs"); - } - - { - auto v = gv::view(pos, "Multiple renderables"); - - // Check multiple Renderables. - gv::view(pos, gv::pick().onLeftClick( - [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) -> gv::picking_result - { - std::cout << "Something has been picked! PICKER 1 " - << "ID: " << (int)face_id << std::endl; - std::cout << "World_Position" << world_pos << std::endl; - std::cout << "Normal" << normal << std::endl; - gv::picking_result res; - res.mPickingColor = tg::color3::cyan; - return res; - })); - - pm::Mesh m2; - m2.copy_from(m); - - pm::vertex_attribute<tg::pos3> pos2(m2); - pos2.copy_from(pos); - - for (auto p : m2.vertices()) - { - pos2[p] = pos2[p] + tg::vec3(2, 2, 2); - } - - gv::view(pos2, gv::pick().onLeftClick( - [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) -> gv::picking_result - { - std::cout << "Something has been picked! PICKER 2 " - << "ID: " << (int)face_id << std::endl; - std::cout << "World_Position" << world_pos << std::endl; - std::cout << "Normal" << normal << std::endl; - gv::picking_result res; - res.mPickingColor = tg::color3::cyan; - return res; - })); - } -} void simple_view(pm::vertex_attribute<tg::pos3> const& pos) { @@ -837,308 +509,604 @@ void viewer_canvas(pm::vertex_attribute<tg::pos3> const& pos) { auto c = gv::canvas(); - tg::rng rng; + tg::rng rng; + + // splats are always world sized and have no default size + c.set_splat_size(0.4f); + + for (auto i = 0; i < 50; ++i) + { + auto p = uniform(rng, tg::boundary_of(tg::sphere3(tg::pos3::zero, 8.f))); + auto n = normalize(p - tg::pos3::zero); + c.add_splat(p, n); + } + } + + // c.add_lines allows segments and tg objects that defines edges(obj) or ranges thereof + // c.add_line is the single line only version + { + auto c = gv::canvas(); + + // meshes work by rendering their edges + c.add_lines(pos); + + // tg::objects that support edges_of(obj) work (and types do not have to be float) + c.add_lines(tg::iaabb3(-10, 10), tg::color3::green); + + // add_line can be used with pos/pos or pos/vec as well + tg::rng rng; + for (auto i = 0; i < 10; ++i) + c.add_line(tg::pos3(1, 2, 3), tg::uniform<tg::dir3>(rng) * uniform(rng, 0.3f, 0.8f), tg::color3::blue); + } + + // c.add_faces allows triangles, quads, meshes, anything with a surface or ranges thereof + // c.add_face is the single line only version + { + auto c = gv::canvas(); + + // meshes work by rendering their faces (assumes planar polygons) + c.add_faces(pos); + + // most finite tg::objects work + c.add_faces(tg::aabb3(-3, -2), tg::color3::red); + + // add_face can be used with pos/pos/pos (triangle) or pos/pos/pos/pos (quad) as well + tg::rng rng; + for (auto i = 0; i < 10; ++i) + { + auto p0 = tg::pos3(1, 2, 3); + auto p1 = p0 + tg::uniform<tg::dir3>(rng) * uniform(rng, 0.3f, 0.8f); + auto p2 = p0 + tg::uniform<tg::dir3>(rng) * uniform(rng, 0.3f, 0.8f); + c.add_face(p0, p1, p2, tg::color3::blue); + } + } + + // return value of add_xyz + // is a reference to all added primitives + // and can be used to change properties + // NOTE: this is only valid until the next add_xyz call + { + auto c = gv::canvas(); + + auto vnormals = pm::vertex_normals_by_area(pos); + auto vcolors = pos.map([](tg::pos3 p) { return tg::color3(abs(p)); }); + + // .color(...) changes the color of all added primitives in that call + c.add_lines(tg::iaabb3(-8, 8)).color(0, 0, 1); + + // .size changes all sizes + // .normal_translate translates each splat in normal direction + // .colors can be used to set different points per splat + c.add_splats(pos, vnormals).size(0.01f).normal_translate(0.03f).colors(vcolors); + + // .color(...) also works with hex strings + c.add_faces(pos).color("#f00"); + } + + // extra features + { + auto c = gv::canvas(); + + c.add_faces(pos); + + // .add_arrow can be used to add arrows + tg::rng rng; + for (auto i = 0; i < 20; ++i) + { + auto d = tg::uniform<tg::dir3>(rng); + + auto end = tg::pos3(d * 2.f); + auto start = end + d * uniform(rng, 0.3f, 0.7f); + + c.add_arrow(start, end, 0.05f, tg::color3::red); + } + + // .add_label can be used to add labels + // normals is optional and can be used for preferred directions + auto normals = pm::vertex_normals_by_area(pos); + for (auto i = 0; i < 20; ++i) + { + auto v = pos.mesh().vertices().random(rng); + c.add_points(pos[v], tg::color3::red); + c.add_label(pos, normals, v, "vertex " + std::to_string(int(v))); + } + } +} + +void interactive_viewer(pm::vertex_attribute<tg::pos3> const& pos) +{ + // gv::interactive([&](float dt) { ... }); + // this opens an interactive viewer + // the lambda is called every frame + // inside, viewer commands can be used, as well as ImGui UI elements + // the scene config and all renderables are hashed and the viewer accumulation is cleared when the hash changes + + // a simple interactive viewer with some interactive-related controls + gv::interactive( + [&](auto) + { + if (ImGui::Button("make screenshot")) + gv::make_screenshot("screenshot.png", 1920, 1080); + + if (ImGui::Button("close viewer")) + gv::close_viewer(); + + gv::view(pos, "interactive viewer with custom buttons. It is slow because nothing is cached."); + }); + + // creating renderables is expensive, cache them whenever possible + // NOTE: capture by value if the interactive viewer is not the top-most viewer + { + auto const r = gv::make_renderable(pos); + gv::interactive( + [r](auto dt) + { + static auto time = 0.f; + time += dt; + + gv::view(r, tg::translation(tg::vec3(tg::sin(tg::radians(time * .5f)) * .5f, 0.f, 0.f)), + "Caching renderables in interactive views increases performance."); + }); + } + + // using imgui in an interactive view + { + auto const r = gv::make_renderable(pos); + gv::interactive( + [r](auto) + { + static float configurable = 0.f; + + ImGui::SliderFloat("Height", &configurable, -3.f, 3.f); + + gv::view(r, tg::translation(tg::vec3(0.f, configurable, 0.f)), "interactive translation of a model"); + }); + } + + // an interactive textured torus with animated texture coordinates + { + glow::SharedTexture2D tex = glow::Texture2D::createFromFile(dataPath + "textures/tiles.color.png", glow::ColorSpace::sRGB); + + // create torus mesh + pm::Mesh m; + auto pos = m.vertices().make_attribute<tg::pos3>(); + auto uv = m.vertices().make_attribute<tg::pos2>(); + pm::objects::add_quad( + m, + [&](pm::vertex_handle v, float x, float y) + { + auto [cx, sx] = tg::sin_cos(tg::pi<float> * 2 * x); + auto [cy, sy] = tg::sin_cos(tg::pi<float> * 2 * y); + + auto const orad = 8.f; + auto const irad = 3.f; + + tg::vec3 t; + t.x = cx; + t.z = sx; + tg::pos3 p; + p.x = orad * cx; + p.y = irad * cy; + p.z = orad * sx; + p += t * irad * sy; + + pos[v] = p; + uv[v] = {1 - x, y}; + }, + 32, 32); + + auto a = 0.f; + auto animate = false; + gv::interactive( + [&](auto dt) + { + ImGui::Begin("Torus"); + ImGui::SliderFloat("angle", &a, 0.f, 360.f); + ImGui::Checkbox("Animate", &animate); + ImGui::End(); + + if (animate) + a += 5 * dt; + + // texture coordinates are rotated by "a" degrees + view(pos, gv::textured(uv, tex).transform(tg::rotation_around(tg::pos2::zero, tg::degree(a))), "animated texture coordinates"); + }); + } +} + +void scenarios() +{ + // in contrast to the other example groups, this group does not introduce new concepts. + // it combines the single features and shows how they can be used in some scenarios + + // debugging / visualizing geometric operations + { + auto g = gv::grid(); + tg::rng rng; + + // construct a random box + const auto c = uniform(rng, tg::sphere3::unit); + auto e0 = uniform_vec(rng, tg::sphere3::unit); + auto e1 = cross(e0, uniform_vec(rng, tg::sphere3::unit)); + auto e2 = cross(e1, e0); + e0 = normalize(e0) * uniform(rng, 0.5f, 0.9f); + e1 = normalize(e1) * uniform(rng, 0.5f, 0.9f); + e2 = normalize(e2) * uniform(rng, 0.5f, 0.9f); + const auto box = tg::box3(c, {e0, e1, e2}); + + // check for some random points if they are inside or outside the box + const auto range = tg::sphere3(tg::pos3::zero, 2.f); + std::vector<tg::pos3> insidePoints; + std::vector<tg::pos3> outsidePoints; + for (auto i = 0; i < 1000; ++i) + { + const auto p = uniform(rng, range); + if (contains(box, p)) + insidePoints.push_back(p); + else + outsidePoints.push_back(p); + } + + { + auto v = gv::view(); + gv::view(box, tg::color4(tg::color3::cyan, 0.5f), "Visualize contained and outside points"); + gv::view(gv::lines(box), tg::color3::cyan); + gv::view(insidePoints, gv::maybe_empty, tg::color3::green); + gv::view(outsidePoints, gv::maybe_empty, tg::color3::red); + } + + // project outside points onto the box + std::vector<tg::segment3> projections; + projections.reserve(outsidePoints.size()); + for (const auto& p : outsidePoints) + projections.emplace_back(p, project(p, box)); - // splats are always world sized and have no default size - c.set_splat_size(0.4f); + { + auto v = gv::view(); + gv::view(box, tg::color3::cyan, "Visualize projection of outside points"); + gv::view(outsidePoints, gv::maybe_empty); + gv::view(projections, gv::maybe_empty); + } - for (auto i = 0; i < 50; ++i) + // measure distance between points and box + std::vector<tg::pos3> points; + std::vector<float> distances; + const auto num = 25; + points.reserve(num); + distances.reserve(num); + for (auto i = 0; i < num; ++i) { - auto p = uniform(rng, tg::boundary_of(tg::sphere3(tg::pos3::zero, 8.f))); - auto n = normalize(p - tg::pos3::zero); - c.add_splat(p, n); + auto p = uniform(rng, range); + points.push_back(p); + distances.push_back(distance(p, box)); + } + + { + auto v = gv::view(); + gv::view(box, tg::color3::cyan, "Use point spheres to visualize distances"); + gv::view(points); + gv::view(gv::points(points).point_size_world(distances)); // point_size specifies the radius of the rendered spheres } } +} - // c.add_lines allows segments and tg objects that defines edges(obj) or ranges thereof - // c.add_line is the single line only version - { - auto c = gv::canvas(); +void vector_graphics() +{ + // TODO: text + // TODO: 2D camera controls - // meshes work by rendering their edges - c.add_lines(pos); + // see Vector2DSample for more sample code + glow::vector::image2D img; + auto g = graphics(img); + g.fill(tg::disk2({100.f, 100.f}, 70), tg::color3::red); + g.draw(tg::circle2({100.f, 100.f}, 70), {tg::color3::black, 2}); + gv::view(img, "2D vector graphics"); +} - // tg::objects that support edges_of(obj) work (and types do not have to be float) - c.add_lines(tg::iaabb3(-10, 10), tg::color3::green); +void custom_renderables() +{ + // TODO +} - // add_line can be used with pos/pos or pos/vec as well - tg::rng rng; - for (auto i = 0; i < 10; ++i) - c.add_line(tg::pos3(1, 2, 3), tg::uniform<tg::dir3>(rng) * uniform(rng, 0.3f, 0.8f), tg::color3::blue); - } +void picking(pm::Mesh& m, pm::vertex_attribute<tg::pos3>& pos, pm::face_attribute<tg::color3> const& col) +{ + // gv::view(obj, args, gv::pick().on...([&](pm::face_index, tg::pos3, tg::vec3){...}), ...); + // adds a Picker to a Renderable (MeshRenderable, PointRenderable, LineRenderable) that enables the picking of individual primitives and the visual representation + // every time a primitive gets picked, the callback function defined in the gv::pick().on...() call will be executed + // picking can be defined on up to two buttons and on hover (gv::pick().onLeftClick(...), gv::pick().onRightClick(...), gv::pick().onHover(...)). + // callback functions need to have the signature as given above and might return a gv::view::picking_result or void. + // gv::view::picking_result as return type can change the visual appearence of the picked primitive via modifying the member attributes pickingColor, borderColor, and borderWidth; + // + // in interactive mode (gv::interactive()) operations on picked primitives can be performed - // c.add_faces allows triangles, quads, meshes, anything with a surface or ranges thereof - // c.add_face is the single line only version { - auto c = gv::canvas(); + auto g = gv::grid(); - // meshes work by rendering their faces (assumes planar polygons) - c.add_faces(pos); + // Only on_left_click callback defined + gv::view(pos, col, + gv::pick().onLeftClick( + [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) -> gv::picking_result + { + std::cout << "Something has been picked! ON_LEFT_CLICK" + << "ID: " << (int)face_id << std::endl; + std::cout << "World_Position " << world_pos << std::endl; + std::cout << "Normal " << normal << std::endl; + gv::picking_result res; + res.pickingColor = tg::color3::cyan; + return res; + }), + "picking MeshRenderable - callback on left click"); - // most finite tg::objects work - c.add_faces(tg::aabb3(-3, -2), tg::color3::red); + // Only on_left_click callback defined - LineRenderable - uncolored + gv::view(gv::lines(pos), + gv::pick().onLeftClick( + [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) + { + std::cout << "Something has been picked! ON_LEFT_CLICK" + << "ID: " << int(face_id) << std::endl; + std::cout << "World_Position" << world_pos << std::endl; + std::cout << "Normal" << normal << std::endl; + }), + "picking LineRenderable - callback on left click"); - // add_face can be used with pos/pos/pos (triangle) or pos/pos/pos/pos (quad) as well - tg::rng rng; - for (auto i = 0; i < 10; ++i) { - auto p0 = tg::pos3(1, 2, 3); - auto p1 = p0 + tg::uniform<tg::dir3>(rng) * uniform(rng, 0.3f, 0.8f); - auto p2 = p0 + tg::uniform<tg::dir3>(rng) * uniform(rng, 0.3f, 0.8f); - c.add_face(p0, p1, p2, tg::color3::blue); + // Only on_left_click callback defined - PointRenderable - uncolored + gv::view(gv::points(pos), + gv::pick().onLeftClick( + [&](pm::vertex_index vertex_id, tg::pos3 world_pos, tg::vec3 normal) + { + std::cout << "Something has been picked! ON_LEFT_CLICK" + << "ID: " << int(vertex_id) << std::endl; + std::cout << "World_Position" << world_pos << std::endl; + std::cout << "Normal" << normal << std::endl; + return; + }), + "picking PointRenderable - callback on left click - spheres"); + } + + { + // Only on_left_click callback defined - PointRenderable - uncolored - square billboards - NOT WORKING APPROPRIATELY + gv::view(gv::points(pos).point_size_world(0.03f).camera_facing().square(), + gv::pick().onLeftClick( + [&](pm::vertex_index vertex_id, tg::pos3 world_pos, tg::vec3 normal) + { + std::cout << "Something has been picked! ON_LEFT_CLICK" + << "ID: " << int(vertex_id) << std::endl; + std::cout << "World_Position" << world_pos << std::endl; + std::cout << "Normal" << normal << std::endl; + return; + }), + "picking PointRenderables - callback on left click - billboards"); } } - // return value of add_xyz - // is a reference to all added primitives - // and can be used to change properties - // NOTE: this is only valid until the next add_xyz call { - auto c = gv::canvas(); - - auto vnormals = pm::vertex_normals_by_area(pos); - auto vcolors = pos.map([](tg::pos3 p) { return tg::color3(abs(p)); }); - - // .color(...) changes the color of all added primitives in that call - c.add_lines(tg::iaabb3(-8, 8)).color(0, 0, 1); + auto g = gv::grid(); - // .size changes all sizes - // .normal_translate translates each splat in normal direction - // .colors can be used to set different points per splat - c.add_splats(pos, vnormals).size(0.01f).normal_translate(0.03f).colors(vcolors); + // Only on_right_click callback defined + gv::view(pos, col, + gv::pick().onRightClick( + [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) + { + std::cout << "Something has been picked! ON_RIGHT_CLICK" + << "ID: " << int(face_id) << std::endl; + return; + }), + "picking MeshRenderable - callback on right click"); - // .color(...) also works with hex strings - c.add_faces(pos).color("#f00"); + // Only on_hover callback defined + gv::view(pos, col, + gv::pick().onHover( + [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) + { + std::cout << "Something has been picked! ON_HOVER" + << "ID: " << (int)face_id << std::endl; + return; + }), + "picking MeshRenderable - callback on hover"); } - // extra features { - auto c = gv::canvas(); - - c.add_faces(pos); + /* Picking in interactive mode with ImGui example */ + pm::face_index face_index_i; + uint32_t face_id_i; + tg::pos3 world_pos_i; + tg::vec3 normal_i; - // .add_arrow can be used to add arrows - tg::rng rng; - for (auto i = 0; i < 20; ++i) - { - auto d = tg::uniform<tg::dir3>(rng); + pm::Mesh m2; + m2.copy_from(m); - auto end = tg::pos3(d * 2.f); - auto start = end + d * uniform(rng, 0.3f, 0.7f); + pm::vertex_attribute<tg::pos3> pos2(m2); + pos2.copy_from(pos); - c.add_arrow(start, end, 0.05f, tg::color3::red); - } + gv::interactive( + [&](auto) + { + gv::view(pos2, col, + gv::pick().onLeftClick( + [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) + { + std::cout << "Something has been picked! ON_LEFT_CLICK" + << "ID: " << int(face_id) << std::endl; + std::cout << "World_Position" << world_pos << std::endl; + std::cout << "Normal" << normal << std::endl; - // .add_label can be used to add labels - // normals is optional and can be used for preferred directions - auto normals = pm::vertex_normals_by_area(pos); - for (auto i = 0; i < 20; ++i) - { - auto v = pos.mesh().vertices().random(rng); - c.add_points(pos[v], tg::color3::red); - c.add_label(pos, normals, v, "vertex " + std::to_string(int(v))); - } - } -} + face_index_i = face_id; + world_pos_i = world_pos; + normal_i = normal; + }), + "picking interactive mode - callback on left click - delete selected face"); -void interactive_viewer(pm::vertex_attribute<tg::pos3> const& pos) -{ - // gv::interactive([&](float dt) { ... }); - // this opens an interactive viewer - // the lambda is called every frame - // inside, viewer commands can be used, as well as ImGui UI elements - // the scene config and all renderables are hashed and the viewer accumulation is cleared when the hash changes + ImGui::Begin("Picking"); - // a simple interactive viewer with some interactive-related controls - gv::interactive( - [&](auto) - { - if (ImGui::Button("make screenshot")) - gv::make_screenshot("screenshot.png", 1920, 1080); + ImGui::Value("pm::face_index", face_index_i.value); - if (ImGui::Button("close viewer")) - gv::close_viewer(); + ImGui::Value("WorldPos.x", world_pos_i.x); + ImGui::Value("WorldPos.y", world_pos_i.y); + ImGui::Value("WorldPos.z", world_pos_i.z); - gv::view(pos, "interactive viewer with custom buttons. It is slow because nothing is cached."); - }); + ImGui::Value("Normal.x", normal_i.x); + ImGui::Value("Normal.y", normal_i.y); + ImGui::Value("Normal.z", normal_i.z); - // creating renderables is expensive, cache them whenever possible - // NOTE: capture by value if the interactive viewer is not the top-most viewer - { - auto const r = gv::make_renderable(pos); - gv::interactive( - [r](auto dt) - { - static auto time = 0.f; - time += dt; + if (ImGui::Button("Delete selected face")) + { + // delete selected face - after deleting one face another face has to be picked + std::cout << "DELETE FACE" << std::endl; + m2.faces().remove(m2.faces()[face_index_i]); + } - gv::view(r, tg::translation(tg::vec3(tg::sin(tg::radians(time * .5f)) * .5f, 0.f, 0.f)), - "Caching renderables in interactive views increases performance."); + ImGui::End(); }); } - // using imgui in an interactive view { - auto const r = gv::make_renderable(pos); + /* Picking in interactive mode with ImGui example.*/ + pm::vertex_index vertex_index_i; + uint32_t vertex_id_i; + tg::pos3 world_pos_i; + tg::vec3 normal_i; + + pm::Mesh m2; + m2.copy_from(m); + + pm::vertex_attribute<tg::pos3> pos2(m2); + pos2.copy_from(pos); + gv::interactive( - [r](auto) + [&](auto) { - static float configurable = 0.f; + gv::view(gv::points(pos2), + gv::pick().onLeftClick( + [&vertex_index_i, &world_pos_i, &normal_i](pm::vertex_index vertex_id, tg::pos3 world_pos, tg::vec3 normal) + { + std::cout << "Something has been picked! ON_LEFT_CLICK" + << "ID: " << int(vertex_id) << std::endl; + std::cout << "World_Position" << world_pos << std::endl; + std::cout << "Normal" << normal << std::endl; - ImGui::SliderFloat("Height", &configurable, -3.f, 3.f); + vertex_index_i = vertex_id; + world_pos_i = world_pos; + normal_i = normal; - gv::view(r, tg::translation(tg::vec3(0.f, configurable, 0.f)), "interactive translation of a model"); - }); - } + return; + }), + "picking interactive mode - PointRenderable"); - // an interactive textured torus with animated texture coordinates - { - glow::SharedTexture2D tex = glow::Texture2D::createFromFile(dataPath + "textures/tiles.color.png", glow::ColorSpace::sRGB); + ImGui::Begin("Picking"); - // create torus mesh - pm::Mesh m; - auto pos = m.vertices().make_attribute<tg::pos3>(); - auto uv = m.vertices().make_attribute<tg::pos2>(); - pm::objects::add_quad( - m, - [&](pm::vertex_handle v, float x, float y) - { - auto [cx, sx] = tg::sin_cos(tg::pi<float> * 2 * x); - auto [cy, sy] = tg::sin_cos(tg::pi<float> * 2 * y); + ImGui::Value("pm::vertex_index", vertex_index_i.value); - auto const orad = 8.f; - auto const irad = 3.f; + ImGui::Value("WorldPos.x", world_pos_i.x); + ImGui::Value("WorldPos.y", world_pos_i.y); + ImGui::Value("WorldPos.z", world_pos_i.z); - tg::vec3 t; - t.x = cx; - t.z = sx; - tg::pos3 p; - p.x = orad * cx; - p.y = irad * cy; - p.z = orad * sx; - p += t * irad * sy; + ImGui::Value("Normal.x", normal_i.x); + ImGui::Value("Normal.y", normal_i.y); + ImGui::Value("Normal.z", normal_i.z); - pos[v] = p; - uv[v] = {1 - x, y}; - }, - 32, 32); + if (ImGui::Button("Delete selected vertex")) + { + // delete selected vertex - after deleting one vertex another vertex has to be picked + std::cout << "DELETE VERTEX" << std::endl; + m2.vertices().remove(m2.vertices()[vertex_index_i]); + } - auto a = 0.f; - auto animate = false; - gv::interactive( - [&](auto dt) - { - ImGui::Begin("Torus"); - ImGui::SliderFloat("angle", &a, 0.f, 360.f); - ImGui::Checkbox("Animate", &animate); ImGui::End(); - - if (animate) - a += 5 * dt; - - // texture coordinates are rotated by "a" degrees - view(pos, gv::textured(uv, tex).transform(tg::rotation_around(tg::pos2::zero, tg::degree(a))), "animated texture coordinates"); }); } -} - -void scenarios() -{ - // in contrast to the other example groups, this group does not introduce new concepts. - // it combines the single features and shows how they can be used in some scenarios - // debugging / visualizing geometric operations { - auto g = gv::grid(); - tg::rng rng; - - // construct a random box - const auto c = uniform(rng, tg::sphere3::unit); - auto e0 = uniform_vec(rng, tg::sphere3::unit); - auto e1 = cross(e0, uniform_vec(rng, tg::sphere3::unit)); - auto e2 = cross(e1, e0); - e0 = normalize(e0) * uniform(rng, 0.5f, 0.9f); - e1 = normalize(e1) * uniform(rng, 0.5f, 0.9f); - e2 = normalize(e2) * uniform(rng, 0.5f, 0.9f); - const auto box = tg::box3(c, {e0, e1, e2}); + // On_hover, on_right_click, and on_left_click callbacks defined simultaneously + gv::view(pos, col, + gv::pick() + .onLeftClick( + [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) + { + std::cout << "Something has been picked! ON_LEFT_CLICK" + << "ID: " << int(face_id) << std::endl; + return; + }) + .onRightClick( + [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) + { + std::cout << "Something has been picked! ON_RIGHT_CLICK" + << "ID: " << int(face_id) << std::endl; + return; + }) + .onHover( + [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) + { + std::cout << "Something has been picked! ON_HOVER" + << "ID: " << int(face_id) << std::endl; + return; + }), + "picking MeshRenderable - multiple callbacks: on left click, on right click, on hover"); + } - // check for some random points if they are inside or outside the box - const auto range = tg::sphere3(tg::pos3::zero, 2.f); - std::vector<tg::pos3> insidePoints; - std::vector<tg::pos3> outsidePoints; - for (auto i = 0; i < 1000; ++i) + { + // User-defined Picking IDs - MeshRenderable + pm::face_attribute<int32_t> fa = pm::face_attribute<int32_t>(m); + int i = 0; + for (auto f : m.faces()) { - const auto p = uniform(rng, range); - if (contains(box, p)) - insidePoints.push_back(p); - else - outsidePoints.push_back(p); + fa[f] = 1000 + i; + i++; } + gv::view(pos, col, + gv::pick(fa).onLeftClick( + [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) -> gv::picking_result + { + std::cout << "Something has been picked! ON_LEFT_CLICK" + << "ID: " << (int)face_id << std::endl; + std::cout << "World_Position" << world_pos << std::endl; + std::cout << "Normal" << normal << std::endl; + gv::picking_result res; + res.pickingColor = tg::color3::cyan; + return res; + }), + "simple picking: callback on left click - user-defined IDs"); + } - { - auto v = gv::view(); - gv::view(box, tg::color4(tg::color3::cyan, 0.5f), "Visualize contained and outside points"); - gv::view(gv::lines(box), tg::color3::cyan); - gv::view(insidePoints, gv::maybe_empty, tg::color3::green); - gv::view(outsidePoints, gv::maybe_empty, tg::color3::red); - } + { + auto v = gv::view(pos, "pciking multiple renderables: different callbacks defined"); - // project outside points onto the box - std::vector<tg::segment3> projections; - projections.reserve(outsidePoints.size()); - for (const auto& p : outsidePoints) - projections.emplace_back(p, project(p, box)); + // Check multiple Renderables. + gv::view(pos, gv::pick().onHover( + [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) -> gv::picking_result + { + std::cout << "Something has been picked! PICKER 1 " + << "ID: " << (int)face_id << std::endl; + std::cout << "World_Position" << world_pos << std::endl; + std::cout << "Normal" << normal << std::endl; + gv::picking_result res; + res.pickingColor = tg::color3::cyan; + return res; + })); - { - auto v = gv::view(); - gv::view(box, tg::color3::cyan, "Visualize projection of outside points"); - gv::view(outsidePoints, gv::maybe_empty); - gv::view(projections, gv::maybe_empty); - } + pm::Mesh m2; + m2.copy_from(m); - // measure distance between points and box - std::vector<tg::pos3> points; - std::vector<float> distances; - const auto num = 25; - points.reserve(num); - distances.reserve(num); - for (auto i = 0; i < num; ++i) - { - auto p = uniform(rng, range); - points.push_back(p); - distances.push_back(distance(p, box)); - } + pm::vertex_attribute<tg::pos3> pos2(m2); + pos2.copy_from(pos); + for (auto p : m2.vertices()) { - auto v = gv::view(); - gv::view(box, tg::color3::cyan, "Use point spheres to visualize distances"); - gv::view(points); - gv::view(gv::points(points).point_size_world(distances)); // point_size specifies the radius of the rendered spheres + pos2[p] = pos2[p] + tg::vec3(2, 2, 2); } - } -} - -void vector_graphics() -{ - // TODO: text - // TODO: 2D camera controls - - // see Vector2DSample for more sample code - glow::vector::image2D img; - auto g = graphics(img); - g.fill(tg::disk2({100.f, 100.f}, 70), tg::color3::red); - g.draw(tg::circle2({100.f, 100.f}, 70), {tg::color3::black, 2}); - gv::view(img, "2D vector graphics"); -} - -void custom_renderables() -{ - // TODO -} -void picking() -{ - // TODO + gv::view(pos2, gv::pick().onLeftClick( + [&](pm::face_index face_id, tg::pos3 world_pos, tg::vec3 normal) -> gv::picking_result + { + std::cout << "Something has been picked! PICKER 2 " + << "ID: " << (int)face_id << std::endl; + std::cout << "World_Position" << world_pos << std::endl; + std::cout << "Normal" << normal << std::endl; + gv::picking_result res; + res.pickingColor = tg::color3::cyan; + return res; + })); + } } void headless_screenshot(pm::vertex_attribute<tg::pos3> const& pos) @@ -1323,11 +1291,10 @@ int main() pm::Mesh m; auto pos = m.vertices().make_attribute<tg::pos3>(); load(dataPath + "suzanne.obj", m, pos); - // pm::objects::add_cube(m, pos); normalize(pos); // make it -1..1*/ auto col = pm::face_attribute<tg::color3>(m); - int it = 0; + // initially random colors for faces (picking sample) auto r = tg::rng(); for (auto x : m.faces()) { @@ -1336,45 +1303,6 @@ int main() col[x] = color; } - pm::Mesh m_2; - auto pos_2 = pm::vertex_attribute<tg::pos3>(m_2); - const auto t1_vh0 = m_2.vertices().add(); - const auto t1_vh1 = m_2.vertices().add(); - const auto t1_vh2 = m_2.vertices().add(); - const auto t2_vh0 = m_2.vertices().add(); - const auto t2_vh1 = m_2.vertices().add(); - const auto t2_vh2 = m_2.vertices().add(); - const auto t3_vh0 = m_2.vertices().add(); - const auto t3_vh1 = m_2.vertices().add(); - const auto t3_vh2 = m_2.vertices().add(); - - pos_2[t1_vh0] = tg::pos3(0, 0, 0); - pos_2[t1_vh1] = tg::pos3(20, 0, 0); - pos_2[t1_vh2] = tg::pos3(0, 20, 0); - pos_2[t2_vh0] = tg::pos3(0, 20, 0); - pos_2[t2_vh1] = tg::pos3(20, 0, 0); - pos_2[t2_vh2] = tg::pos3(20, 20, 0); - pos_2[t3_vh0] = tg::pos3(20, 20, 0); - pos_2[t3_vh1] = tg::pos3(40, 20, 0); - pos_2[t3_vh2] = tg::pos3(40, 0, 0); - - auto col_2 = pm::face_attribute<tg::color3>(m_2); - const auto f1 = m_2.faces().add(t1_vh0, t1_vh1, t1_vh2); - const auto f2 = m_2.faces().add(t2_vh0, t2_vh1, t2_vh2); - const auto f3 = m_2.faces().add(t3_vh0, t3_vh1, t3_vh2); - - col_2[f1] = tg::color3::blue; - col_2[f2] = tg::color3::cyan; - col_2[f2] = tg::color3::magenta; - - // deduplicate(m, pos); - // m.compactify(); - - // simple picking demo - { - simple_picking(m, pos, col); - } - // basic demos { simple_view(pos); @@ -1405,6 +1333,8 @@ int main() { vector_graphics(); + picking(m, pos, col); + custom_renderables(); special_use_cases(pos); @@ -1418,4 +1348,4 @@ int main() known_issues(pos); return EXIT_SUCCESS; -} +} \ No newline at end of file -- GitLab