├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake └── CreateImGuiTarget.cmake ├── demo.png └── src ├── CMakeLists.txt ├── OsgImGuiHandler.cpp ├── OsgImGuiHandler.hpp └── main.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(imgui-osg C CXX) 3 | 4 | # Fetch and create imgui target 5 | include(${CMAKE_CURRENT_LIST_DIR}/cmake/CreateImGuiTarget.cmake) 6 | CreateImGuiTarget( 7 | IMPLEMENTATION OpenGL3 8 | REPOSITORY https://github.com/ocornut/imgui.git 9 | TAG v1.85 10 | ) 11 | 12 | # Prerequisites: 13 | find_package(OpenSceneGraph REQUIRED COMPONENTS osgViewer osgGA) 14 | 15 | add_subdirectory(src) 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alexey Galitsyn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImGui-OSG 2 | This application shows how to use [OpenSceneGraph](https://github.com/openscenegraph/OpenSceneGraph) and [Dear ImGui](https://github.com/ocornut/imgui) together. 3 | ![demo](demo.png) 4 | 5 | ## Prerequisites 6 | This example requires: 7 | - OpenSceneGraph 8 | - OpenGL 9 | 10 | You may install them using vcpkg on Windows or apt on Ubuntu. 11 | 12 | ## How to build 13 | ``` 14 | mkdir build && cd build 15 | cmake .. 16 | cmake --build . 17 | ``` 18 | -------------------------------------------------------------------------------- /cmake/CreateImGuiTarget.cmake: -------------------------------------------------------------------------------- 1 | # Downloads sources and creates cmake target for Dear ImGui 2 | # 3 | # Dear ImGui is not a cmake project (yet?) so we have to manually 4 | # add it's sources and compile it within our project. 5 | # 6 | # Arguments: 7 | # REPOSITORY - git repository to download sources from 8 | # TAG - git tag or branch, e.g. origin/master 9 | # IMPLEMENTATION - ImGui implementation, check imgui examples dir 10 | # 11 | # Result: 12 | # Creates target imgui::imgui with all required dependecies. Just 13 | # link with it and you are good to go! 14 | 15 | include(CMakeFindDependencyMacro) 16 | include(FetchContent) 17 | function(CreateImGuiTarget) 18 | # Define the supported set of keywords 19 | set(prefix imgui) 20 | set(noValues) 21 | set(singleValues LOADER IMPLEMENTATION REPOSITORY TAG) 22 | set(multiValues) 23 | 24 | # Process the arguments passed in 25 | include(CMakeParseArguments) 26 | cmake_parse_arguments(${prefix} 27 | "${noValues}" 28 | "${singleValues}" 29 | "${multiValues}" 30 | ${ARGN} 31 | ) 32 | 33 | #message(STATUS "ImGui LOADER = " ${imgui_LOADER}) 34 | message(STATUS "ImGui IMPLEMENTATION = " ${imgui_IMPLEMENTATION}) 35 | message(STATUS "ImGui REPOSITORY = " ${imgui_REPOSITORY}) 36 | message(STATUS "ImGui TAG = " ${imgui_TAG}) 37 | 38 | if(NOT imgui_REPOSITORY OR NOT imgui_TAG) 39 | message(FATAL_ERROR "Please, specify imgui repository and tag") 40 | endif() 41 | 42 | string(TOLOWER "${imgui_LOADER}" imgui_LOADER_lower ) 43 | string(TOLOWER "${imgui_IMPLEMENTATION}" imgui_IMPLEMENTATION_lower ) 44 | 45 | FetchContent_Declare(imgui 46 | GIT_REPOSITORY "${imgui_REPOSITORY}" 47 | GIT_TAG "${imgui_TAG}" 48 | GIT_SHALLOW YES 49 | ) 50 | 51 | FetchContent_GetProperties(imgui) 52 | 53 | if(NOT imgui_POPULATED) 54 | FetchContent_Populate(imgui) 55 | endif() 56 | 57 | # Core ImGui stuff: 58 | add_library(imgui INTERFACE) 59 | add_library(imgui::imgui ALIAS imgui) 60 | target_sources(imgui INTERFACE 61 | ${imgui_SOURCE_DIR}/imgui.cpp 62 | ${imgui_SOURCE_DIR}/imgui_demo.cpp 63 | ${imgui_SOURCE_DIR}/imgui_draw.cpp 64 | ${imgui_SOURCE_DIR}/imgui_widgets.cpp 65 | ${imgui_SOURCE_DIR}/imgui_tables.cpp 66 | ) 67 | target_include_directories(imgui INTERFACE 68 | ${imgui_SOURCE_DIR} 69 | ${imgui_SOURCE_DIR}/backends 70 | ${imgui_SOURCE_DIR}/examples 71 | ) 72 | 73 | # OpenGL loader (obsolette since ImGui has it's own loader since v1.80) 74 | # if(imgui_LOADER_lower STREQUAL "glew") 75 | # find_dependency(GLEW) 76 | # target_link_libraries(imgui 77 | # INTERFACE 78 | # GLEW::GLEW 79 | # ) 80 | # target_compile_definitions(imgui 81 | # INTERFACE 82 | # IMGUI_IMPL_OPENGL_LOADER_GLEW 83 | # ) 84 | # TODO: add some other loaders... 85 | # elseif(imgui_LOADER_lower STREQUAL "gl3w") 86 | # target_compile_definitions(imgui 87 | # INTERFACE 88 | # IMGUI_IMPL_OPENGL_LOADER_GL3W 89 | # ) 90 | # else() 91 | # message(FATAL_ERROR "Unknown ImGui loader ${imgui_LOADER}") 92 | # endif() 93 | 94 | # Implementation 95 | if(imgui_IMPLEMENTATION_lower STREQUAL "opengl3") 96 | find_dependency(OpenGL) 97 | target_link_libraries(imgui 98 | INTERFACE 99 | OpenGL::GL 100 | ) 101 | target_sources(imgui 102 | INTERFACE 103 | ${imgui_SOURCE_DIR}/backends/imgui_impl_opengl3.h 104 | ${imgui_SOURCE_DIR}/backends/imgui_impl_opengl3.cpp 105 | ) 106 | # TODO: add other implementations... 107 | else() 108 | message(FATAL_ERROR "Unknown ImGui implementation ${imgui_IMPLEMENTATION}") 109 | endif() 110 | endfunction() -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tordan/imgui-osg/ce38e025edbceca5ced7335cceb7b9aa7491ac9b/demo.png -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(imgui-osg-example 2 | main.cpp 3 | OsgImGuiHandler.hpp 4 | OsgImGuiHandler.cpp 5 | ) 6 | 7 | target_link_libraries(imgui-osg-example 8 | PRIVATE 9 | imgui::imgui 10 | ${OPENSCENEGRAPH_LIBRARIES} 11 | ) 12 | 13 | target_include_directories(imgui-osg-example 14 | PRIVATE 15 | ${OPENSCENEGRAPH_INCLUDE_DIRS} 16 | ) -------------------------------------------------------------------------------- /src/OsgImGuiHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "OsgImGuiHandler.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "imgui.h" 10 | #include "imgui_impl_opengl3.h" 11 | 12 | struct OsgImGuiHandler::ImGuiNewFrameCallback : public osg::Camera::DrawCallback 13 | { 14 | ImGuiNewFrameCallback(OsgImGuiHandler& handler) 15 | : handler_(handler) 16 | { 17 | } 18 | 19 | void operator()(osg::RenderInfo& renderInfo) const override 20 | { 21 | handler_.newFrame(renderInfo); 22 | } 23 | 24 | private: 25 | OsgImGuiHandler& handler_; 26 | }; 27 | 28 | struct OsgImGuiHandler::ImGuiRenderCallback : public osg::Camera::DrawCallback 29 | { 30 | ImGuiRenderCallback(OsgImGuiHandler& handler) 31 | : handler_(handler) 32 | { 33 | } 34 | 35 | void operator()(osg::RenderInfo& renderInfo) const override 36 | { 37 | handler_.render(renderInfo); 38 | } 39 | 40 | private: 41 | OsgImGuiHandler& handler_; 42 | }; 43 | 44 | OsgImGuiHandler::OsgImGuiHandler() 45 | : time_(0.0f), mousePressed_{false}, mouseWheel_(0.0f), initialized_(false) 46 | { 47 | IMGUI_CHECKVERSION(); 48 | ImGui::CreateContext(); 49 | ImGuiIO& io = ImGui::GetIO(); 50 | (void)io; 51 | init(); 52 | } 53 | 54 | /** 55 | * Imporant Note: Dear ImGui expects the control Keys indices not to be 56 | * greater thant 511. It actually uses an array of 512 elements. However, 57 | * OSG has indices greater than that. So here I do a conversion for special 58 | * keys between ImGui and OSG. 59 | */ 60 | static int ConvertFromOSGKey(int key) 61 | { 62 | using KEY = osgGA::GUIEventAdapter::KeySymbol; 63 | 64 | switch (key) 65 | { 66 | case KEY::KEY_Tab: 67 | return ImGuiKey_Tab; 68 | case KEY::KEY_Left: 69 | return ImGuiKey_LeftArrow; 70 | case KEY::KEY_Right: 71 | return ImGuiKey_RightArrow; 72 | case KEY::KEY_Up: 73 | return ImGuiKey_UpArrow; 74 | case KEY::KEY_Down: 75 | return ImGuiKey_DownArrow; 76 | case KEY::KEY_Page_Up: 77 | return ImGuiKey_PageUp; 78 | case KEY::KEY_Page_Down: 79 | return ImGuiKey_PageDown; 80 | case KEY::KEY_Home: 81 | return ImGuiKey_Home; 82 | case KEY::KEY_End: 83 | return ImGuiKey_End; 84 | case KEY::KEY_Delete: 85 | return ImGuiKey_Delete; 86 | case KEY::KEY_BackSpace: 87 | return ImGuiKey_Backspace; 88 | case KEY::KEY_Return: 89 | return ImGuiKey_Enter; 90 | case KEY::KEY_Escape: 91 | return ImGuiKey_Escape; 92 | default: // Not found 93 | return -1; 94 | } 95 | } 96 | 97 | void OsgImGuiHandler::init() 98 | { 99 | ImGuiIO& io = ImGui::GetIO(); 100 | 101 | // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. 102 | io.KeyMap[ImGuiKey_Tab] = ImGuiKey_Tab; 103 | io.KeyMap[ImGuiKey_LeftArrow] = ImGuiKey_LeftArrow; 104 | io.KeyMap[ImGuiKey_RightArrow] = ImGuiKey_RightArrow; 105 | io.KeyMap[ImGuiKey_UpArrow] = ImGuiKey_UpArrow; 106 | io.KeyMap[ImGuiKey_DownArrow] = ImGuiKey_DownArrow; 107 | io.KeyMap[ImGuiKey_PageUp] = ImGuiKey_PageUp; 108 | io.KeyMap[ImGuiKey_PageDown] = ImGuiKey_PageDown; 109 | io.KeyMap[ImGuiKey_Home] = ImGuiKey_Home; 110 | io.KeyMap[ImGuiKey_End] = ImGuiKey_End; 111 | io.KeyMap[ImGuiKey_Delete] = ImGuiKey_Delete; 112 | io.KeyMap[ImGuiKey_Backspace] = ImGuiKey_Backspace; 113 | io.KeyMap[ImGuiKey_Enter] = ImGuiKey_Enter; 114 | io.KeyMap[ImGuiKey_Escape] = ImGuiKey_Escape; 115 | io.KeyMap[ImGuiKey_A] = osgGA::GUIEventAdapter::KeySymbol::KEY_A; 116 | io.KeyMap[ImGuiKey_C] = osgGA::GUIEventAdapter::KeySymbol::KEY_C; 117 | io.KeyMap[ImGuiKey_V] = osgGA::GUIEventAdapter::KeySymbol::KEY_V; 118 | io.KeyMap[ImGuiKey_X] = osgGA::GUIEventAdapter::KeySymbol::KEY_X; 119 | io.KeyMap[ImGuiKey_Y] = osgGA::GUIEventAdapter::KeySymbol::KEY_Y; 120 | io.KeyMap[ImGuiKey_Z] = osgGA::GUIEventAdapter::KeySymbol::KEY_Z; 121 | } 122 | 123 | void OsgImGuiHandler::setCameraCallbacks(osg::Camera* camera) 124 | { 125 | camera->setPreDrawCallback(new ImGuiNewFrameCallback(*this)); 126 | camera->setPostDrawCallback(new ImGuiRenderCallback(*this)); 127 | } 128 | 129 | void OsgImGuiHandler::newFrame(osg::RenderInfo& renderInfo) 130 | { 131 | ImGui_ImplOpenGL3_NewFrame(); 132 | 133 | ImGuiIO& io = ImGui::GetIO(); 134 | 135 | osg::Viewport* viewport = renderInfo.getCurrentCamera()->getViewport(); 136 | io.DisplaySize = ImVec2(viewport->width(), viewport->height()); 137 | 138 | double currentTime = renderInfo.getView()->getFrameStamp()->getSimulationTime(); 139 | io.DeltaTime = currentTime - time_ + 0.0000001; 140 | time_ = currentTime; 141 | 142 | for (int i = 0; i < 3; i++) 143 | { 144 | io.MouseDown[i] = mousePressed_[i]; 145 | } 146 | 147 | io.MouseWheel = mouseWheel_; 148 | mouseWheel_ = 0.0f; 149 | 150 | ImGui::NewFrame(); 151 | } 152 | 153 | void OsgImGuiHandler::render(osg::RenderInfo&) 154 | { 155 | drawUi(); 156 | ImGui::Render(); 157 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 158 | } 159 | 160 | bool OsgImGuiHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) 161 | { 162 | if (!initialized_) 163 | { 164 | auto view = aa.asView(); 165 | if (view) 166 | { 167 | setCameraCallbacks(view->getCamera()); 168 | initialized_ = true; 169 | } 170 | } 171 | 172 | ImGuiIO& io = ImGui::GetIO(); 173 | const bool wantCaptureMouse = io.WantCaptureMouse; 174 | const bool wantCaptureKeyboard = io.WantCaptureKeyboard; 175 | 176 | switch (ea.getEventType()) 177 | { 178 | case osgGA::GUIEventAdapter::KEYDOWN: 179 | case osgGA::GUIEventAdapter::KEYUP: 180 | { 181 | const bool isKeyDown = ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN; 182 | const int c = ea.getKey(); 183 | const int special_key = ConvertFromOSGKey(c); 184 | if (special_key > 0) 185 | { 186 | assert((special_key >= 0 && special_key < 512) && "ImGui KeysMap is an array of 512"); 187 | 188 | io.KeysDown[special_key] = isKeyDown; 189 | 190 | io.KeyCtrl = ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_CTRL; 191 | io.KeyShift = ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_SHIFT; 192 | io.KeyAlt = ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_ALT; 193 | io.KeySuper = ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_SUPER; 194 | } 195 | else if (isKeyDown && c > 0 && c < 0xFF) 196 | { 197 | io.AddInputCharacter((unsigned short)c); 198 | } 199 | return wantCaptureKeyboard; 200 | } 201 | case (osgGA::GUIEventAdapter::RELEASE): 202 | case (osgGA::GUIEventAdapter::PUSH): 203 | { 204 | io.MousePos = ImVec2(ea.getX(), io.DisplaySize.y - ea.getY()); 205 | mousePressed_[0] = ea.getButtonMask() & osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON; 206 | mousePressed_[1] = ea.getButtonMask() & osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON; 207 | mousePressed_[2] = ea.getButtonMask() & osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON; 208 | return wantCaptureMouse; 209 | } 210 | case (osgGA::GUIEventAdapter::DRAG): 211 | case (osgGA::GUIEventAdapter::MOVE): 212 | { 213 | io.MousePos = ImVec2(ea.getX(), io.DisplaySize.y - ea.getY()); 214 | return wantCaptureMouse; 215 | } 216 | case (osgGA::GUIEventAdapter::SCROLL): 217 | { 218 | mouseWheel_ = ea.getScrollingMotion() == osgGA::GUIEventAdapter::SCROLL_UP ? 1.0 : -1.0; 219 | return wantCaptureMouse; 220 | } 221 | default: 222 | { 223 | return false; 224 | } 225 | } 226 | 227 | return false; 228 | } 229 | -------------------------------------------------------------------------------- /src/OsgImGuiHandler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace osg { 6 | class Camera; 7 | } 8 | 9 | class OsgImGuiHandler : public osgGA::GUIEventHandler 10 | { 11 | public: 12 | OsgImGuiHandler(); 13 | 14 | bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) override; 15 | 16 | protected: 17 | // Put your ImGui code inside this function 18 | virtual void drawUi() = 0; 19 | 20 | private: 21 | void init(); 22 | 23 | void setCameraCallbacks(osg::Camera* camera); 24 | 25 | void newFrame(osg::RenderInfo& renderInfo); 26 | 27 | void render(osg::RenderInfo& renderInfo); 28 | 29 | private: 30 | struct ImGuiNewFrameCallback; 31 | struct ImGuiRenderCallback; 32 | 33 | double time_; 34 | bool mousePressed_[3]; 35 | float mouseWheel_; 36 | bool initialized_; 37 | }; 38 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "OsgImGuiHandler.hpp" 10 | 11 | class ImGuiInitOperation : public osg::Operation 12 | { 13 | public: 14 | ImGuiInitOperation() 15 | : osg::Operation("ImGuiInitOperation", false) 16 | { 17 | } 18 | 19 | void operator()(osg::Object* object) override 20 | { 21 | osg::GraphicsContext* context = dynamic_cast(object); 22 | if (!context) 23 | return; 24 | 25 | if (!ImGui_ImplOpenGL3_Init()) 26 | { 27 | std::cout << "ImGui_ImplOpenGL3_Init() failed\n"; 28 | } 29 | } 30 | }; 31 | 32 | class ImGuiDemo : public OsgImGuiHandler 33 | { 34 | protected: 35 | void drawUi() override 36 | { 37 | // ImGui code goes here... 38 | ImGui::ShowDemoWindow(); 39 | } 40 | }; 41 | 42 | int main(int argc, char** argv) 43 | { 44 | osgViewer::Viewer viewer; 45 | viewer.apply(new osgViewer::SingleWindow(100, 100, 640, 480)); 46 | viewer.setRealizeOperation(new ImGuiInitOperation); 47 | viewer.addEventHandler(new ImGuiDemo); 48 | 49 | return viewer.run(); 50 | } 51 | --------------------------------------------------------------------------------