diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c027b32..2f1eff56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,6 +135,7 @@ add_subdirectory(apps/mandeye_single_session_viewer) add_subdirectory(apps/multi_view_tls_registration) add_subdirectory(apps/multi_view_tls_registration_legacy) add_subdirectory(apps/multi_session_registration) +add_subdirectory(apps/multi_session_registration_legacy) if(BUILD_WITH_UTILITY_APPLICATION) add_subdirectory(apps/manual_color) diff --git a/apps/multi_session_registration/CMakeLists.txt b/apps/multi_session_registration/CMakeLists.txt index bd9be085..89be3fdf 100644 --- a/apps/multi_session_registration/CMakeLists.txt +++ b/apps/multi_session_registration/CMakeLists.txt @@ -6,7 +6,7 @@ project(multi_session_registration_step_3) set(SOURCES multi_session_registration.cpp multi_session_factor_graph.cpp - "../../core/src/utils.cpp" + raylib_utils.cpp ) # Windows: add resource file @@ -41,13 +41,14 @@ target_include_directories( target_link_libraries( multi_session_registration_step_3 # PRIVATE ${THIRDPARTY_DIRECTORY}/glew-2.2.0/lib/Release/x64/glew32s.lib - ${FREEGLUT_LIBRARY} - ${OPENGL_gl_LIBRARY} - OpenGL::GLU + # raylib-based like step 2; core_raylib brings in core, raylib and ScanRenderer. + core_raylib + raylib_widgets + imgui_raylib + rlimgui + imguizmo_raylib ${PLATFORM_LASZIP_LIB} - ${PLATFORM_MISCELLANEOUS_LIBS} - ${CORE_LIBRARIES} - ${GUI_LIBRARIES}) + ${PLATFORM_MISCELLANEOUS_LIBS}) if(WIN32) add_custom_command( diff --git a/apps/multi_session_registration/multi_session_registration.cpp b/apps/multi_session_registration/multi_session_registration.cpp index ca6a4bbf..dcb78e46 100644 --- a/apps/multi_session_registration/multi_session_registration.cpp +++ b/apps/multi_session_registration/multi_session_registration.cpp @@ -2,13 +2,11 @@ #include #include -#include -#include #include +#include #include -#include #include #include #include @@ -23,19 +21,27 @@ #include #include #include -#include +#ifdef _WIN32 +// windows.h (pulled in by portable-file-dialogs.h) declares CloseWindow/ShowCursor like raylib.h does. +#define CloseWindow CloseWindow_win32 +#define ShowCursor ShowCursor_win32 +#endif #include +#ifdef _WIN32 +#undef CloseWindow +#undef ShowCursor +#endif #include #ifdef _WIN32 #include "resource.h" -#include #endif #include "multi_session_factor_graph.h" +#include "raylib_utils.h" std::string winTitle = std::string("Step 3 (Multi session registration) ") + HDMAPPING_VERSION_STRING; @@ -87,7 +93,7 @@ static const std::vector appShortcuts = { { "Normal keys", "A", " { "", "Shift+R", "" }, { "", "S", "" }, { "", "Ctrl+S", "Save project" }, - { "", "Ctrl+Shift+S", "" }, + { "", "Ctrl+Shift+S", "Save project as" }, { "", "T", "" }, { "", "Ctrl+T", "" }, { "", "U", "" }, @@ -167,6 +173,9 @@ struct ProjectSettings std::vector session_file_names; }; +// Project file opened or last saved; empty until then (Save project falls back to Save project as). +std::string project_file_name; + std::vector edges; int index_active_edge = -1; bool manipulate_active_edge = false; @@ -364,6 +373,35 @@ void ndt_gui() #endif } +// Places the edge gizmo at the active edge's target pose. +void setGizmoFromActiveEdge() +{ + const Edge& e = edges[index_active_edge]; + const Eigen::Affine3d m_to = sessions[e.index_session_from].point_clouds_container.point_clouds[e.index_from].m_pose * + affine_matrix_from_pose_tait_bryan(e.relative_pose_tb); + Eigen::Map gizmo(m_gizmo); + gizmo = m_to.matrix().cast(); +} + +// Makes edge `i` active. The manipulate-edge view draws the scans picked by first/second_session_index and +// index_loop_closure_source/target, so these follow the edge; a gizmo that is on is moved to the new edge, +// otherwise it would write the previous edge's pose into this one. +void setActiveEdge(int i) +{ + if (i < 0 || i >= (int)edges.size()) + return; + + index_active_edge = i; + const Edge& e = edges[i]; + first_session_index = e.index_session_from; + second_session_index = e.index_session_to; + index_loop_closure_source = e.index_from; + index_loop_closure_target = e.index_to; + + if (edge_gizmo) + setGizmoFromActiveEdge(); +} + void loop_closure_gui() { if (ImGui::Begin("Manual Pose Graph Loop Closure Mode", &is_loop_closure_gui, ImGuiWindowFlags_AlwaysAutoResize)) @@ -462,9 +500,39 @@ void loop_closure_gui() std::string number_active_edges = "number_edges: " + std::to_string(edges.size()); ImGui::Text(number_active_edges.c_str()); + + // Switching edges is blocked while a session gizmo is on, as for the index_active_edge input below. + bool is_session_gizmo = false; + for (const auto& s : sessions) + is_session_gizmo |= s.is_gizmo; + + ImGui::BeginDisabled(is_session_gizmo); + for (int i = 0; i < (int)edges.size(); i++) + { + const auto& e = edges[i]; + // session:scan -> session:scan + ImGui::Text( + "%s edge %d: %d:%d -> %d:%d", + (manipulate_active_edge && i == index_active_edge) ? ">" : " ", + i, + e.index_session_from, + e.index_from, + e.index_session_to, + e.index_to); + ImGui::SameLine(); + char buttonLabel[128]; + snprintf(buttonLabel, sizeof(buttonLabel), "Set Active##%d", i); + if (ImGui::Button(buttonLabel)) + { + setActiveEdge(i); + manipulate_active_edge = true; + } + } + ImGui::EndDisabled(); if (edges.size() > 0) { - ImGui::Checkbox("manipulate_active_edge", &manipulate_active_edge); + if (ImGui::Checkbox("manipulate_active_edge", &manipulate_active_edge) && manipulate_active_edge) + setActiveEdge(std::clamp(index_active_edge, 0, (int)edges.size() - 1)); if (manipulate_active_edge) { int remove_edge_index = -1; @@ -494,6 +562,8 @@ void loop_closure_gui() index_active_edge = 0; if (index_active_edge >= (int)edges.size()) index_active_edge = (int)edges.size() - 1; + if (index_active_edge != prev_index_active_edge) + setActiveEdge(index_active_edge); } } @@ -516,37 +586,15 @@ void loop_closure_gui() } edges = new_edges; - index_active_edge = remove_edge_index - 1; + index_active_edge = edges.empty() ? -1 : std::clamp(remove_edge_index - 1, 0, (int)edges.size() - 1); manipulate_active_edge = false; } - bool prev_gizmo = edge_gizmo; + const bool prev_gizmo = edge_gizmo; ImGui::Checkbox("gizmo", &edge_gizmo); if (prev_gizmo != edge_gizmo) - { - auto m_to = sessions[edges[index_active_edge].index_session_from] - .point_clouds_container.point_clouds[edges[index_active_edge].index_from] - .m_pose * - affine_matrix_from_pose_tait_bryan(edges[index_active_edge].relative_pose_tb); - - m_gizmo[0] = (float)m_to(0, 0); - m_gizmo[1] = (float)m_to(1, 0); - m_gizmo[2] = (float)m_to(2, 0); - m_gizmo[3] = (float)m_to(3, 0); - m_gizmo[4] = (float)m_to(0, 1); - m_gizmo[5] = (float)m_to(1, 1); - m_gizmo[6] = (float)m_to(2, 1); - m_gizmo[7] = (float)m_to(3, 1); - m_gizmo[8] = (float)m_to(0, 2); - m_gizmo[9] = (float)m_to(1, 2); - m_gizmo[10] = (float)m_to(2, 2); - m_gizmo[11] = (float)m_to(3, 2); - m_gizmo[12] = (float)m_to(0, 3); - m_gizmo[13] = (float)m_to(1, 3); - m_gizmo[14] = (float)m_to(2, 3); - m_gizmo[15] = (float)m_to(3, 3); - } + setGizmoFromActiveEdge(); if (!edge_gizmo) { if (ImGui::Button("ICP")) @@ -1787,16 +1835,15 @@ bool loadProject(const std::string& file_name, ProjectSettings& _project_setting } std::cout << "Found " << edges.size() << "edges\nOpening done\n"; - - return true; } catch (std::exception& e) { std::cout << "can't load project settings: " << e.what() << std::endl; return false; } + project_file_name = file_name; std::string newTitle = winTitle + " - " + truncPath(file_name); - glutSetWindowTitle(newTitle.c_str()); + SetWindowTitle(newTitle.c_str()); loaded_sessions = false; time_stamp_offset = 0.0; @@ -1815,17 +1862,31 @@ void openProject() } } -void saveProject() +void saveProjectTo(const std::string& output_file_name) +{ + if (save_project_settings(fs::path(output_file_name).string(), project_settings)) + { + project_file_name = output_file_name; + std::string newTitle = winTitle + " - " + truncPath(output_file_name); + SetWindowTitle(newTitle.c_str()); + } +} + +void saveProjectAs() { std::string output_file_name = ""; output_file_name = mandeye::fd::SaveFileDialog("Save project file", mandeye::fd::Project_filter, ".mjp", "project"); if (output_file_name.size() > 0) - if (save_project_settings(fs::path(output_file_name).string(), project_settings)) - { - std::string newTitle = winTitle + " - " + truncPath(output_file_name); - glutSetWindowTitle(newTitle.c_str()); - } + saveProjectTo(output_file_name); +} + +void saveProject() +{ + if (project_file_name.empty()) + saveProjectAs(); + else + saveProjectTo(project_file_name); } void addSession() @@ -1845,10 +1906,82 @@ void addSession() } } -void loadSessions() +void appendSession(const std::string& ps); +void finishLoadingSessions(); +void loadSessions(); + +// Adds a session file to the project unless it is already there; returns whether it was added. +bool addSessionFile(const std::string& path) +{ + const auto& names = project_settings.session_file_names; + if (std::find(names.begin(), names.end(), path) != names.end()) + { + std::cout << "Session file already in project: '" << path << "'" << std::endl; + return false; + } + std::cout << "Adding session file: '" << path << "'" << std::endl; + project_settings.session_file_names.push_back(path); + return true; +} + +// Drag & drop: a project (*.mjp) replaces the current one; session files (*.mjs, *.json) are added and loaded +// right away (appended to already loaded sessions, which keep their in-memory state). +void loadDroppedFiles() +{ + FilePathList dropped_files = LoadDroppedFiles(); + std::vector paths(dropped_files.paths, dropped_files.paths + dropped_files.count); + UnloadDroppedFiles(dropped_files); + + auto ext_of = [](const fs::path& p) + { + std::string ext = p.extension().string(); + std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); + return ext; + }; + + for (const auto& path : paths) + { + if (ext_of(path) == ".mjp") + { + loadProject(path.string(), project_settings); + return; + } + } + + std::vector added; + for (const auto& path : paths) + { + const std::string ext = ext_of(path); + if (ext != ".mjs" && ext != ".json") + { + std::cout << "Ignoring dropped file: '" << path.string() << "'" << std::endl; + continue; + } + if (addSessionFile(path.string())) + added.push_back(path.string()); + } + + if (added.empty()) + { + pfd::message("Unsupported file", "Drop a project (*.mjp) or session files (*.mjs, *.json).", pfd::choice::ok, pfd::icon::warning); + return; + } + + if (loaded_sessions) + { + for (const auto& ps : added) + appendSession(ps); + finishLoadingSessions(); + } + else + { + time_stamp_offset = 0.0; + loadSessions(); + } +} + +void appendSession(const std::string& ps) { - sessions.clear(); - for (const auto& ps : project_settings.session_file_names) { Session session; session.load(fs::path(ps).string(), is_decimate, bucket_x, bucket_y, bucket_z, calculate_offset); @@ -1871,8 +2004,11 @@ void loadSessions() if (session.is_ground_truth) index_gt = sessions.size() - 1; } - loaded_sessions = true; +} +// Reorders (ground truth first), recolors and re-uploads the loaded sessions. +void finishLoadingSessions() +{ // reorder std::vector sessions_reorder; std::vector session_file_names_reordered; @@ -1913,6 +2049,9 @@ void loadSessions() std::cout << "session: '" << s.session_file_name << "' ground truth [" << int(s.is_ground_truth) << "]" << std::endl; } + assignDistinctSessionColors(sessions); + invalidateSessionRenderers(); + // update time_stamp_offset std::cout << "update time_stamp_offset" << std::endl; for (const auto& s : sessions) @@ -1930,6 +2069,16 @@ void loadSessions() } } +void loadSessions() +{ + sessions.clear(); + for (const auto& ps : project_settings.session_file_names) + appendSession(ps); + loaded_sessions = true; + + finishLoadingSessions(); +} + void generate_loop_closures(const std::vector& sessions, std::vector& edges) { edges.clear(); @@ -2539,15 +2688,16 @@ void settings_gui() void display() { + syncSessionRenderers(sessions); + ImGuiIO& io = ImGui::GetIO(); - glViewport(0, 0, (GLsizei)io.DisplaySize.x, (GLsizei)io.DisplaySize.y); + rlViewport(0, 0, GetRenderWidth(), GetRenderHeight()); - glClearColor(bg_color.x * bg_color.w, bg_color.y * bg_color.w, bg_color.z * bg_color.w, bg_color.w); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glEnable(GL_DEPTH_TEST); + ClearBackground(ColorFromNormalized(Vector4{ bg_color.x * bg_color.w, bg_color.y * bg_color.w, bg_color.z * bg_color.w, bg_color.w })); + rlEnableDepthTest(); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); + rlMatrixMode(RL_PROJECTION); + rlLoadIdentity(); float ratio = float(io.DisplaySize.x) / float(io.DisplaySize.y); updateCameraTransition(); @@ -2564,8 +2714,7 @@ void display() if (!is_ortho) { - reshape((GLsizei)io.DisplaySize.x, (GLsizei)io.DisplaySize.y); - glTranslatef(translate_x, translate_y, translate_z); + reshape((int)io.DisplaySize.x, (int)io.DisplaySize.y); // janusz if (is_loop_closure_gui) @@ -2642,10 +2791,11 @@ void display() viewLocal.translate(-rotation_center); - glLoadMatrixf(viewLocal.matrix().data()); + rlMultMatrixf(viewLocal.matrix().data()); } else updateOrthoView(); + captureFrameMatrices(); showAxes(); @@ -2698,7 +2848,9 @@ void display() // sessions[first_session_index].point_clouds_container.point_clouds.at(i).point_size = gui_point_size; - sessions[first_session_index].point_clouds_container.point_clouds.at(i).render( + renderScanAtPose( + first_session_index, + i, m_src, viewer_decimate_point_cloud, viewer_reduce_rendered_trajectory, @@ -2723,7 +2875,9 @@ void display() Eigen::Affine3d m_trg = _m_trg * (m_trg_0.inverse() * m_trg_curr); // sessions[second_session_index].point_clouds_container.point_clouds.at(i).point_size = gui_point_size; - sessions[second_session_index].point_clouds_container.point_clouds.at(i).render( + renderScanAtPose( + second_session_index, + i, m_trg, viewer_decimate_point_cloud, viewer_reduce_rendered_trajectory, @@ -2760,7 +2914,9 @@ void display() // false); Eigen::Affine3d m_src = sessions[first_session_index].point_clouds_container.point_clouds.at(i).m_pose; - sessions[first_session_index].point_clouds_container.point_clouds.at(i).render( + renderScan( + first_session_index, + i, false, observation_picking, viewer_decimate_point_cloud, @@ -2797,7 +2953,9 @@ void display() // false); Eigen::Affine3d m_src = sessions[second_session_index].point_clouds_container.point_clouds.at(i).m_pose; - sessions[second_session_index].point_clouds_container.point_clouds.at(i).render( + renderScan( + second_session_index, + i, false, observation_picking, viewer_decimate_point_cloud, @@ -2813,37 +2971,37 @@ void display() // sessions[first_session_index].point_clouds_container.render(); - glBegin(GL_LINE_STRIP); + beginLineStrip(); for (auto& pc : sessions[first_session_index].point_clouds_container.point_clouds) { - glColor3f(pc.render_color[0], pc.render_color[1], pc.render_color[2]); - glVertex3f(pc.m_pose(0, 3), pc.m_pose(1, 3), pc.m_pose(2, 3)); + color3f(pc.render_color[0], pc.render_color[1], pc.render_color[2]); + lineStripVertex3f(pc.m_pose(0, 3), pc.m_pose(1, 3), pc.m_pose(2, 3)); } - glEnd(); + endLineStrip(); int i = 0; for (auto& pc : sessions[first_session_index].point_clouds_container.point_clouds) { - glColor3f(pc.render_color[0], pc.render_color[1], pc.render_color[2]); - glRasterPos3f(pc.m_pose(0, 3), pc.m_pose(1, 3), pc.m_pose(2, 3) + 0.1); - glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, (const unsigned char*)std::to_string(i).c_str()); + color3f(pc.render_color[0], pc.render_color[1], pc.render_color[2]); + labelPos3f(pc.m_pose(0, 3), pc.m_pose(1, 3), pc.m_pose(2, 3) + 0.1); + labelText(std::to_string(i).c_str()); i++; } - glBegin(GL_LINE_STRIP); + beginLineStrip(); for (auto& pc : sessions[second_session_index].point_clouds_container.point_clouds) { - glColor3f(pc.render_color[0], pc.render_color[1], pc.render_color[2]); - glVertex3f(pc.m_pose(0, 3), pc.m_pose(1, 3), pc.m_pose(2, 3)); + color3f(pc.render_color[0], pc.render_color[1], pc.render_color[2]); + lineStripVertex3f(pc.m_pose(0, 3), pc.m_pose(1, 3), pc.m_pose(2, 3)); } - glEnd(); + endLineStrip(); i = 0; for (auto& pc : sessions[second_session_index].point_clouds_container.point_clouds) { - glColor3f(pc.render_color[0], pc.render_color[1], pc.render_color[2]); - glRasterPos3f(pc.m_pose(0, 3), pc.m_pose(1, 3), pc.m_pose(2, 3) + 0.1); - glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, (const unsigned char*)std::to_string(i).c_str()); + color3f(pc.render_color[0], pc.render_color[1], pc.render_color[2]); + labelPos3f(pc.m_pose(0, 3), pc.m_pose(1, 3), pc.m_pose(2, 3) + 0.1); + labelText(std::to_string(i).c_str()); i++; } @@ -2854,19 +3012,19 @@ void display() int index_src = sessions[i].pose_graph_loop_closure.edges[j].index_from; int index_trg = sessions[i].pose_graph_loop_closure.edges[j].index_to; - glColor3f(0.0f, 0.0f, 1.0f); - glBegin(GL_LINES); + color3f(0.0f, 0.0f, 1.0f); + rlBegin(RL_LINES); auto v1 = sessions[i].point_clouds_container.point_clouds.at(index_src).m_pose.translation(); auto v2 = sessions[i].point_clouds_container.point_clouds.at(index_trg).m_pose.translation(); - glVertex3f(v1.x(), v1.y(), v1.z()); - glVertex3f(v2.x(), v2.y(), v2.z()); + rlVertex3f(v1.x(), v1.y(), v1.z()); + rlVertex3f(v2.x(), v2.y(), v2.z()); - glVertex3f((v1.x() + v2.x()) * 0.5, (v1.y() + v2.y()) * 0.5, (v1.z() + v2.z()) * 0.5); - glVertex3f((v1.x() + v2.x()) * 0.5, (v1.y() + v2.y()) * 0.5, (v1.z() + v2.z()) * 0.5 + 10); - glEnd(); + rlVertex3f((v1.x() + v2.x()) * 0.5, (v1.y() + v2.y()) * 0.5, (v1.z() + v2.z()) * 0.5); + rlVertex3f((v1.x() + v2.x()) * 0.5, (v1.y() + v2.y()) * 0.5, (v1.z() + v2.z()) * 0.5 + 10); + rlEnd(); - glRasterPos3f((v1.x() + v2.x()) * 0.5, (v1.y() + v2.y()) * 0.5, (v1.z() + v2.z()) * 0.5 + 10 + 0.1); - glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, (const unsigned char*)std::to_string(j).c_str()); + labelPos3f((v1.x() + v2.x()) * 0.5, (v1.y() + v2.y()) * 0.5, (v1.z() + v2.z()) * 0.5 + 10 + 0.1); + labelText(std::to_string(j).c_str()); } } @@ -2879,22 +3037,22 @@ void display() int index_session_to = edges[i].index_session_to; if (sessions[index_session_from].is_ground_truth || sessions[index_session_to].is_ground_truth) - glColor3f(0.0f, 1.0f, 1.0f); + color3f(0.0f, 1.0f, 1.0f); else - glColor3f(1.0f, 1.0f, 0.0f); + color3f(1.0f, 1.0f, 0.0f); - glBegin(GL_LINES); + rlBegin(RL_LINES); auto v1 = sessions[index_session_from].point_clouds_container.point_clouds.at(index_src).m_pose.translation(); auto v2 = sessions[index_session_to].point_clouds_container.point_clouds.at(index_trg).m_pose.translation(); - glVertex3f(v1.x(), v1.y(), v1.z()); - glVertex3f(v2.x(), v2.y(), v2.z()); + rlVertex3f(v1.x(), v1.y(), v1.z()); + rlVertex3f(v2.x(), v2.y(), v2.z()); - glVertex3f((v1.x() + v2.x()) * 0.5, (v1.y() + v2.y()) * 0.5, (v1.z() + v2.z()) * 0.5); - glVertex3f((v1.x() + v2.x()) * 0.5, (v1.y() + v2.y()) * 0.5, (v1.z() + v2.z()) * 0.5 + 10); - glEnd(); + rlVertex3f((v1.x() + v2.x()) * 0.5, (v1.y() + v2.y()) * 0.5, (v1.z() + v2.z()) * 0.5); + rlVertex3f((v1.x() + v2.x()) * 0.5, (v1.y() + v2.y()) * 0.5, (v1.z() + v2.z()) * 0.5 + 10); + rlEnd(); - glRasterPos3f((v1.x() + v2.x()) * 0.5, (v1.y() + v2.y()) * 0.5, (v1.z() + v2.z()) * 0.5 + 10 + 0.1); - glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, (const unsigned char*)std::to_string(i).c_str()); + labelPos3f((v1.x() + v2.x()) * 0.5, (v1.y() + v2.y()) * 0.5, (v1.z() + v2.z()) * 0.5 + 10 + 0.1); + labelText(std::to_string(i).c_str()); } } else @@ -2903,9 +3061,9 @@ void display() { if (session.visible) { - session.point_clouds_container.render(observation_picking, viewer_decimate_point_cloud, viewer_reduce_rendered_trajectory); - session.ground_control_points.render(session.point_clouds_container); - session.control_points.render(session.point_clouds_container, false); + renderSession(session, observation_picking, viewer_decimate_point_cloud, viewer_reduce_rendered_trajectory); + renderGroundControlPoints(session.ground_control_points, session.point_clouds_container); + renderControlPoints(session.control_points, session.point_clouds_container); //// int index_point_clouds = -1; @@ -2932,11 +3090,11 @@ void display() { if (index_local_trajectory < session.point_clouds_container.point_clouds[index_point_clouds].local_trajectory.size()) { - glColor3f( + color3f( session.point_clouds_container.point_clouds[index_point_clouds].render_color[0], session.point_clouds_container.point_clouds[index_point_clouds].render_color[1], session.point_clouds_container.point_clouds[index_point_clouds].render_color[2]); - glBegin(GL_LINES); + rlBegin(RL_LINES); auto m1 = session.point_clouds_container.point_clouds[index_point_clouds].m_pose; auto m2 = @@ -2944,16 +3102,16 @@ void display() auto v1 = (m1 * m2).translation(); - glVertex3f(v1.x() - 5.0, v1.y(), v1.z()); - glVertex3f(v1.x() + 5.0, v1.y(), v1.z()); + rlVertex3f(v1.x() - 5.0, v1.y(), v1.z()); + rlVertex3f(v1.x() + 5.0, v1.y(), v1.z()); - glVertex3f(v1.x(), v1.y() - 5.0, v1.z()); - glVertex3f(v1.x(), v1.y() + 5.0, v1.z()); + rlVertex3f(v1.x(), v1.y() - 5.0, v1.z()); + rlVertex3f(v1.x(), v1.y() + 5.0, v1.z()); - glVertex3f(v1.x(), v1.y(), v1.z() - 5.0); - glVertex3f(v1.x(), v1.y(), v1.z() + 5.0); + rlVertex3f(v1.x(), v1.y(), v1.z() - 5.0); + rlVertex3f(v1.x(), v1.y(), v1.z() + 5.0); - glEnd(); + rlEnd(); } } } @@ -3023,9 +3181,7 @@ void display() // gnss.render(session.point_clouds_container); - ImGui_ImplOpenGL2_NewFrame(); - ImGui_ImplGLUT_NewFrame(); - ImGui::NewFrame(); + rlImGuiBegin(); ShowMainDockSpace(); @@ -3054,11 +3210,11 @@ void display() if (!is_ortho) { - GLfloat projection[16]; - glGetFloatv(GL_PROJECTION_MATRIX, projection); + float projection[16]; + getProjectionMatrix(projection); - GLfloat modelview[16]; - glGetFloatv(GL_MODELVIEW_MATRIX, modelview); + float modelview[16]; + getModelviewMatrix(modelview); ImGuizmo::Manipulate( modelview, @@ -3192,11 +3348,11 @@ void display() if (!is_ortho) { - GLfloat projection[16]; - glGetFloatv(GL_PROJECTION_MATRIX, projection); + float projection[16]; + getProjectionMatrix(projection); - GLfloat modelview[16]; - glGetFloatv(GL_MODELVIEW_MATRIX, modelview); + float modelview[16]; + getModelviewMatrix(modelview); ImGuizmo::Manipulate( &modelview[0], @@ -3438,6 +3594,9 @@ pose_tait_bryan_from_affine_matrix(m_src.inverse() * m_g); view_kbd_shortcuts(); + if (IsFileDropped()) + loadDroppedFiles(); + if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_A, false)) { addSession(); @@ -3477,9 +3636,13 @@ pose_tait_bryan_from_affine_matrix(m_src.inverse() * m_g); if (sessions.size() > 0) if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_S, false)) { - saveProject(); + if (io.KeyShift) + saveProjectAs(); + else + saveProject(); // workaround + io.AddKeyEvent(ImGuiMod_Shift, false); io.AddKeyEvent(ImGuiKey_S, false); io.AddKeyEvent(ImGuiMod_Ctrl, false); } @@ -3492,6 +3655,10 @@ pose_tait_bryan_from_affine_matrix(m_src.inverse() * m_g); openProject(); if (ImGui::MenuItem("Save project", "Ctrl+S", nullptr, project_settings.session_file_names.size() > 0)) saveProject(); + if (ImGui::MenuItem("Save project as...", "Ctrl+Shift+S", nullptr, project_settings.session_file_names.size() > 0)) + saveProjectAs(); + if (ImGui::IsItemHovered() && !project_file_name.empty()) + ImGui::SetTooltip("Current project: %s", project_file_name.c_str()); ImGui::Separator(); @@ -4088,6 +4255,7 @@ pose_tait_bryan_from_affine_matrix(m_src.inverse() * m_g); ImGui::Text("Colors:"); ImGui::ColorEdit3("Background", (float*)&bg_color, ImGuiColorEditFlags_NoInputs); + pointsColorMenu(); ImGui::Separator(); @@ -4228,6 +4396,8 @@ pose_tait_bryan_from_affine_matrix(m_src.inverse() * m_g); info_window(infoLines, appShortcuts); + end3DAndDrawLabels(); + if (compass_ruler) drawMiniCompassWithRuler(); @@ -4248,32 +4418,12 @@ pose_tait_bryan_from_affine_matrix(m_src.inverse() * m_g); if (is_settings_gui) settings_gui(); - ImGui::Render(); - ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); - - glutSwapBuffers(); - glutPostRedisplay(); + rlImGuiEnd(); } void mouse(int glut_button, int state, int x, int y) { ImGuiIO& io = ImGui::GetIO(); - io.MousePos = ImVec2((float)x, (float)y); - int button = -1; - if (glut_button == GLUT_LEFT_BUTTON) - button = 0; - if (glut_button == GLUT_RIGHT_BUTTON) - button = 1; - if (glut_button == GLUT_MIDDLE_BUTTON) - button = 2; - if (button != -1 && state == GLUT_DOWN) - io.MouseDown[button] = true; - if (button != -1 && state == GLUT_UP) - io.MouseDown[button] = false; - - static int glutMajorVersion = glutGet(GLUT_VERSION) / 10000; - if (state == GLUT_DOWN && (glut_button == 3 || glut_button == 4) && glutMajorVersion < 3) - wheel(glut_button, glut_button == 3 ? 1 : -1, x, y); if (!io.WantCaptureMouse) { @@ -4344,9 +4494,12 @@ int main(int argc, char* argv[]) { std::cout << winTitle << "\n\n" << "USAGE:\n" - << std::filesystem::path(argv[0]).stem().string() << " /?\n\n" + << std::filesystem::path(argv[0]).stem().string() + << " [--mjp ] [--mjs ...] [ ...] /?\n\n" << "where\n" - << " Path to Mandeye JSON Project file (*.mjp)\n" + << " --mjp Mandeye JSON Project file to open\n" + << " --mjs [...] Session file(s) (*.mjs, *.json) to add to the project and load\n" + << " *.mjp opens a project, *.mjs / *.json adds a session\n" << " -h, /h, --help, /? Show this help and exit\n\n"; return 0; @@ -4354,27 +4507,54 @@ int main(int argc, char* argv[]) initGL(&argc, argv, winTitle, display, mouse); - if (argc > 1) + // --mjp / --mjs take the following non-flag arguments; bare arguments are classified by extension. + std::string project_file; + std::vector session_files; + std::string current_flag; + for (int i = 1; i < argc; i++) { - for (int i = 1; i < argc; i++) + const std::string arg(argv[i]); + if (arg == "--mjp" || arg == "--mjs") + { + current_flag = arg; + continue; + } + if (arg.rfind("--", 0) == 0) { - std::string ext = fs::path(argv[i]).extension().string(); - std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); + std::cerr << "Unknown option: '" << arg << "'" << std::endl; + current_flag.clear(); + continue; + } - if (ext == ".mjp") - { - loadProject(argv[i], project_settings); + std::string ext = fs::path(arg).extension().string(); + std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); - break; - } + if (current_flag == "--mjp" || (current_flag.empty() && ext == ".mjp")) + { + if (project_file.empty()) + project_file = arg; + else + std::cerr << "Only one project can be opened, ignoring: '" << arg << "'" << std::endl; + current_flag.clear(); } + else if (current_flag == "--mjs" || ext == ".mjs" || ext == ".json") + session_files.push_back(arg); + else + std::cerr << "Ignoring argument: '" << arg << "'" << std::endl; } - glutMainLoop(); + if (!project_file.empty()) + loadProject(project_file, project_settings); + + bool added = false; + for (const auto& session_file : session_files) + added |= addSessionFile(session_file); + if (added) + loadSessions(); + + mainLoop(); - ImGui_ImplOpenGL2_Shutdown(); - ImGui_ImplGLUT_Shutdown(); - ImGui::DestroyContext(); + shutdownGL(); } catch (const std::bad_alloc& e) { std::cerr << "System is out of memory : " << e.what() << std::endl; diff --git a/apps/multi_session_registration/raylib_utils.cpp b/apps/multi_session_registration/raylib_utils.cpp new file mode 100644 index 00000000..c7b8a55c --- /dev/null +++ b/apps/multi_session_registration/raylib_utils.cpp @@ -0,0 +1,759 @@ +#include "raylib_utils.h" + +#include "raymath.h" +#include "rlImGui.h" + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +raylib_widgets::OrbitCamera camera; + +// GLUT step 3 used 1000 (every point was a glVertex call); GPU buffers draw full density, as in step 2. +int viewer_decimate_point_cloud = 2; +int mouse_old_x = 0, mouse_old_y = 0; +int mouse_buttons = 0; +bool& is_ortho = camera.isOrtho; +bool& lock_z = camera.lockZ; +bool show_axes = true; +ImVec4 bg_color = ImVec4(0.65f, 0.65f, 0.65f, 1.00f); +int point_size = 1; +bool info_gui = false; +bool compass_ruler = true; +bool cor_gui = false; +Eigen::Affine3f viewLocal = Eigen::Affine3f::Identity(); + +Eigen::Map rotation_center(&camera.euler.rotationCenter.x); +float& rotate_x = camera.euler.rotateX; +float& rotate_y = camera.euler.rotateY; +float& translate_x = camera.euler.translate.x; +float& translate_y = camera.euler.translate.y; +float& translate_z = camera.euler.translate.z; +Eigen::Map new_rotation_center(&camera.eulerGoal.rotationCenter.x); +float& new_rotate_x = camera.eulerGoal.rotateX; +float& new_rotate_y = camera.eulerGoal.rotateY; +float& new_translate_x = camera.eulerGoal.translate.x; +float& new_translate_y = camera.eulerGoal.translate.y; +float& new_translate_z = camera.eulerGoal.translate.z; +bool& camera_transition_active = camera.eulerTransitionActive; +float* const m_ortho_projection = camera.orthoProjection; +float* const m_ortho_gizmo_view = camera.orthoGizmoView; + +namespace +{ + void (*display_cb)() = nullptr; + void (*mouse_cb)(int, int, int, int) = nullptr; + + Matrix frame_mvp{}; + float current_color[3] = { 1.f, 1.f, 1.f }; + + struct StripVertex + { + Vector3 p; + float c[3]; + }; + std::vector strip; + + struct Label + { + Vector3 p; + std::string text; + Color color; + }; + Vector3 label_pos{}; + float label_color[3] = { 1.f, 1.f, 1.f }; + std::vector