[POC] Replace GLUT with Raylib - #467
Merged
Merged
Conversation
Replaces GLUT + legacy immediate-mode OpenGL with raylib in place, in multi_view_tls_registration_gui.cpp: same globals, function names, and structure as before, but windowing/input/camera/picking/mini-compass (previously from the shared core/src/utils.cpp, which other GLUT apps still depend on and so can't be changed) are now local re-implementations using rlgl's rl*() legacy-GL-emulation API. Point-cloud and loop-closure rendering (previously core's shared legacy-GL .render() methods) go through a new core_raylib target's ScanRenderer instead, which also adds per-point jet-colormap gradient coloring by intensity, height, or distance from the rotation center (view menu), and pose/edge index number labels in the loop closure overlay -- none of which the GLUT app had. GNSS/ControlPoints/GroundControlPoints 3D rendering and the observation- picking Intersection wireframes are not yet ported (their ImGui panels still work); noted with comments at each call site. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgxCVLnEEAftKKRvZHPUB9
Moves the ~1300 lines of camera/picking/mini-compass/misc-ImGui-widget code (this app's raylib-based replacement for the shared core/src/utils.cpp API) out of multi_view_tls_registration_gui.cpp into their own rl_utils.cpp/rl_utils.h files, mirroring how core/src/utils.cpp / Core/utils.hpp were structured for the GLUT apps. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgxCVLnEEAftKKRvZHPUB9
Contributor
Author
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgxCVLnEEAftKKRvZHPUB9
raylib (new dependency, fetched via cmake/raylib.cmake for the multi_view_tls_registration raylib port) builds GLFW from source, which needs libxrandr-dev/libxinerama-dev/libxcursor-dev/libxext-dev to configure its X11 backend -- CI's minimal images don't have these by default, unlike this dev machine. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgxCVLnEEAftKKRvZHPUB9
raylib.h's DrawText/CloseWindow/ShowCursor collide with windows.h's identically-named GDI macro and WinUser functions once both headers land in the same translation unit (something this app never did before, since the original never included raylib.h). Fixed with NOGDI/NOUSER before <windows.h> in rl_utils.cpp (the only file that still needs windows.h, for ShellExecuteA), pinned with clang-format off/on since alphabetical reordering silently reintroduces the windows.h-before-shellapi.h ordering bug this also needs to avoid. gui.cpp's own <windows.h> include is dropped entirely -- nothing in it actually used anything from windows.h. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgxCVLnEEAftKKRvZHPUB9
The previous fix only covered rl_utils.cpp. gui.cpp itself pulls in real windows.h transitively via portable-file-dialogs.h (needed for SendMessage/DispatchMessage/MessageBoxW/GetActiveWindow, so NOGDI/NOUSER isn't an option here like it was in rl_utils.cpp), which collides with raylib.h's CloseWindow(void)/ShowCursor(void) declarations -- a hard extern-"C"-redeclaration error regardless of include order, not just a macro issue. Renamed raylib's versions via macro just around its own #include, and #undef DrawText after the last include (windows.h #defines it to DrawTextA) so plain DrawText/CloseWindow calls later in the file keep meaning raylib's functions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgxCVLnEEAftKKRvZHPUB9
The previous attempt renamed raylib.h's CloseWindow/ShowCursor around its own #include, which avoided the compile-time redeclaration clash but broke the link: the compiled raylib library still only exports the symbol under its real name, so the renamed *declaration* just became an unresolved external symbol. Flipped it around: rename windows.h's versions instead (scoped to just the portable-file-dialogs.h include, which is what actually pulls windows.h in) -- safe since portable-file-dialogs.h itself never calls CloseWindow/ShowCursor, so nothing there breaks, and raylib's real CloseWindow/ShowCursor stay callable normally everywhere else in the file, matching what's actually in the library. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgxCVLnEEAftKKRvZHPUB9
Collaborator
|
Nice. I would suggest to display GL_RENDERER string in the app bar. Although RAYLib prints it some people might not look at the console output at all and this string is helpfull especially on systems with multiple GPU as OpenGL uses some default GPU and might choose iGPU instead of dGPU. Just debug thing to let the user know that they shoul probably set their OpenGL default renderer to high performace card in system settings. |
Stakeholder request: apps/multi_view_tls_registration_legacy is a full, independent copy of step2 as it was before the GLUT-to-raylib port (restored from the pre-port commit) -- new target multi_view_tls_registration_step_2_legacy, built side by side with the raylib-based multi_view_tls_registration_step_2. Not sharing translation units between the two, so they can diverge or be retired independently. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgxCVLnEEAftKKRvZHPUB9
On some GPU/driver combinations (seen with an NVIDIA PRIME-offloaded
context) GLFW's GLX request for a 4x-multisample framebuffer fails
outright ("GLX: Failed to create context: BadValue"), and raylib/GLFW
then segfaults using the broken context instead of degrading
gracefully. Not worth the crash risk for a cosmetic antialiasing hint.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XgxCVLnEEAftKKRvZHPUB9
Surfaces GPU renderer info (raylib has no wrapper for it) plus per-frame draw-call/vertex counts from ScanRenderer::draw(), replacing the plain FPS counter. ScanRenderer tracks its own counts since neither raylib nor rlgl expose draw-call/vertex stats for custom (non-batched) glDrawArrays calls. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgxCVLnEEAftKKRvZHPUB9
Windows: force GLFW_USE_HYBRID_HPG ON so the raylib build exports NvOptimusEnablement/AmdPowerXpressRequestHighPerformance, the standard symbol hint NVIDIA/AMD drivers read from an EXE to pick the discrete GPU on Optimus/PowerXpress laptops. No build-time equivalent exists on Linux (GPU selection there is a runtime PRIME choice), so add a README note pointing Linux users at prime-run instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgxCVLnEEAftKKRvZHPUB9
Contributor
Author
|
good idea @mwlasiuk added: Also I've tested Windows build. |
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgxCVLnEEAftKKRvZHPUB9
prime-run isn't always installed even when prime-select is, and prime-select nvidia alone doesn't force offload mode per-launch -- give the underlying env vars as the primary instructions, prime-run as a shorthand when available. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XgxCVLnEEAftKKRvZHPUB9
Contributor
Author
|
Janusz tested macOS. It runs but draws incorectly. We need to address it |
GetLaserBeam() queried rlgl's matrix stack live, but mouse() runs before display() each frame, so it was reading the previous frame's post-end3DMatrixStack() state (identity modelview, 2D ortho projection) instead of the 3D camera -- every pick ray was garbage, so Ctrl+click picking (GCP, loop closure, translate tool) would grab an arbitrary point and the camera-transition would fly there, looking like the scene exploding. display() now caches the 3D view/projection into frame_view_3d/frame_proj_3d right before end3DMatrixStack() resets the stack, and GetLaserBeam() reads those instead. Also ports the three remaining legacy-GL render() functions (shared with the GLUT apps in `core`, so they can't be touched) to raylib: GroundControlPoints, GNSS, and ControlPoints -- markers/lines via DrawLine3D/rlBegin(RL_LINES), text labels via 2D screen-space DrawText projected through frame_mvp_3d. ControlPoints additionally restricts scan_renderer.draw() to the active scan while editing, since the bulk multi-scan draw is skipped entirely in that mode. Fixes a label-overlap bug found along the way (GCP/Control Points labels sitting centimeters apart in world space collapsed to the same screen pixels) by giving drawOutlinedText() a `line` param so nearby labels stack instead of overlapping, and restores full scan visibility when Manual Loop Closure closes (nothing previously undid the source/target-only filtering it applies while open). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Contributor
Author
Signed-off-by: Michal <michal@Michals-MacBook-Air.local>
michalpelka
marked this pull request as ready for review
July 30, 2026 12:38
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


To say that HDMapping use legacy method to draw is to say nothing.
multi_view_tls_registration (step2) was still running on GLUT plus immediate-mode OpenGL straight out of the 90s — glBegin/glVertex, gluPerspective, gluUnProject, raster-position bitmap fonts, the lot. This PR moves it to raylib, in place, keeping the app's existing structure (globals, function
names, control flow) as close to the original as swapping the rendering backend allows.
#What changed
own rl_utils.cpp/rl_utils.h, built on rlgl's legacy-GL-emulation API (a software matrix stack + immediate-mode layer) instead of real gl*/glu*/glut* calls.
Porting was done with Sonnet 5 with Claude Code.
New dependencies and licensing
Two new third-party deps come in via cmake/raylib.cmake (FetchContent): raylib and rlImGui, both zlib-licensed — same permissive family as this project's MIT license, no new licensing friction. Dear ImGui itself isn't duplicated: imgui_raylib builds from the same already-vendored 3rdparty/imgui tree
the GLUT apps' imgui target uses (it's on the docking branch, which ShowMainDockSpace() needs), just without the GLUT/OpenGL2 backend files.
Strucutre
Raylib has thin abstraction for OpenGL, that is very handy in this project. It introduce intermediate mode, emulates old GL2 mechanism making conversion quite simple.
The split deliberately mirrors the project's existing architecture rather than inventing a new one:
Anyone already familiar with the GLUT apps' structure should find this one recognizable, not foreign.
Pictures
What to do next
It is not ready to be merged - the code is AI slop. But working prototype with managable diff.
I would like to continue, but I need to: