/* -*-c++-*- */ /* osgEarth - Geospatial SDK for OpenSceneGraph * Copyright 2018 Pelican Mapping * http://osgearth.org * * osgEarth is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace osgEarth { // base class for GUI elements that allows you to toggle visibility // no export. header only. class ImGuiPanel // no export { public: void setVisible(bool value) { _visible = value; dirtySettings(); } //! whether it's current shown bool isVisible() const { return _visible; } //! name of the GUI panel const char* name() const { return _name.c_str(); } //! render this GUI virtual void draw(osg::RenderInfo& ri) { } //! override to set custom values in the .ini file virtual void load(const osgEarth::Config&) { } //! override to get custom values from the .ini file virtual void save(osgEarth::Config&) { } protected: ImGuiPanel(const char* name) : _name(name), _visible(false), _last_visible(false) { //nop } //! convenience function for finding nodes template T* findNode(osg::RenderInfo& ri) const { return osgEarth::findTopMostNodeOfType(ri.getCurrentCamera()); } //! convenience function for getting the current view inline osgViewer::View* view(osg::RenderInfo& ri) const { return dynamic_cast(ri.getView()); } //! convenience function for getting the current camera inline osg::Camera* camera(osg::RenderInfo& ri) const { return ri.getCurrentCamera(); } //! convenience function for getting the topmost stateset inline osg::StateSet* stateset(osg::RenderInfo& ri) const { return ri.getCurrentCamera()->getOrCreateStateSet(); } //! convenience function for finding nodes template bool findNode(osg::observer_ptr& node, osg::RenderInfo& ri) const { if (!node.valid()) node = osgEarth::findTopMostNodeOfType(ri.getCurrentCamera()); return node.valid(); } //! convenience function for finding nodes template bool findNodeOrHide(osg::observer_ptr& node, osg::RenderInfo& ri) { if (!node.valid()) node = osgEarth::findTopMostNodeOfType(ri.getCurrentCamera()); if (!node.valid()) setVisible(false); return node.valid(); } template bool findLayer(osg::observer_ptr& layer, osg::RenderInfo& ri) { if (!layer.valid()) { MapNode* mapNode = osgEarth::findTopMostNodeOfType(ri.getCurrentCamera()); if (mapNode) layer = mapNode->getMap()->getLayer(); } return layer.valid(); } template bool findLayerOrHide(osg::observer_ptr& layer, osg::RenderInfo& ri) { if (!layer.valid()) { MapNode* mapNode = osgEarth::findTopMostNodeOfType(ri.getCurrentCamera()); if (mapNode) layer = mapNode->getMap()->getLayer(); } if (!layer.valid()) setVisible(false); return layer.valid(); } //! sets a value and dirties the .ini store template void set_and_dirty(A& var, const B& value) { if (var != value) { var = value; dirtySettings(); } } //! Map point under the mouse cursor GeoPoint getPointAtMouse(MapNode* mapNode, osg::View* v, float x, float y) { GeoPoint point; osg::Vec3d world; if (mapNode->getTerrain()->getWorldCoordsUnderMouse(v, x, y, world)) point.fromWorld(mapNode->getMapSRS(), world); return point; } private: std::string _name; bool _visible; bool _last_visible; bool* _dirtySettings = nullptr; friend class ImGuiAppEngine; public: bool* visible() { return &_visible; } //! override to set custom values in the .ini file void load_base(const osgEarth::Config& conf) { conf.get("Visible", _visible); load(conf); } //! override to set custom values in the .ini file void save_base(osgEarth::Config& conf) { conf.set("Visible", _visible); save(conf); } //! whether the visibility changed (and resets the flag) bool visibilityChanged() { bool whether = _visible != _last_visible; _last_visible = _visible; return whether; } void dirtySettings() { if (_dirtySettings) *_dirtySettings = true; } }; } // various additions to ImGui. namespace ImGuiLTable { static bool Begin(const char* name, int flags = 0) { bool ok = ImGui::BeginTable(name, 2, ImGuiTableFlags_SizingFixedFit | flags); if (ok) { ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide); ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_WidthStretch); } return ok; } static bool SliderFloat(const char* label, float* v, float v_min, float v_max) { ImGui::TableNextColumn(); ImGui::Text(label); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1); std::string s("##" + std::string(label)); return ImGui::SliderFloat(s.c_str(), v, v_min, v_max); } static bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) { ImGui::TableNextColumn(); ImGui::Text(label); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1); std::string s("##" + std::string(label)); return ImGui::SliderFloat(s.c_str(), v, v_min, v_max, format, flags); } static bool SliderDouble(const char* label, double* v, double v_min, double v_max, const char* format = nullptr) { ImGui::TableNextColumn(); ImGui::Text(label); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1); std::string s("##" + std::string(label)); float temp = *v; bool ok = ImGui::SliderFloat(s.c_str(), &temp, (float)v_min, (float)v_max, format); if (ok) *v = (double)temp; return ok; } static bool SliderDouble(const char* label, double* v, double v_min, double v_max, const char* format, ImGuiSliderFlags flags) { ImGui::TableNextColumn(); ImGui::Text(label); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1); std::string s("##" + std::string(label)); float temp = *v; bool ok = ImGui::SliderFloat(s.c_str(), &temp, (float)v_min, (float)v_max, format, flags); if (ok) *v = (double)temp; return ok; } static bool SliderInt(const char* label, int* v, int v_min, int v_max) { ImGui::TableNextColumn(); ImGui::Text(label); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1); std::string s("##" + std::string(label)); return ImGui::SliderInt(s.c_str(), v, v_min, v_max); } static bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) { ImGui::TableNextColumn(); ImGui::Text(label); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1); std::string s("##" + std::string(label)); return ImGui::SliderInt(s.c_str(), v, v_min, v_max, format, flags); } static bool Checkbox(const char* label, bool* v) { ImGui::TableNextColumn(); ImGui::Text(label); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1); std::string s("##" + std::string(label)); return ImGui::Checkbox(s.c_str(), v); } static bool BeginCombo(const char* label, const char* defaultItem) { ImGui::TableNextColumn(); ImGui::Text(label); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1); std::string s("##" + std::string(label)); return ImGui::BeginCombo(s.c_str(), defaultItem); } static void EndCombo() { return ImGui::EndCombo(); } static bool InputFloat(const char* label, float* v) { ImGui::TableNextColumn(); ImGui::Text(label); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1); std::string s("##" + std::string(label)); return ImGui::InputFloat(s.c_str(), v); } template static void Text(const char* label, const char* format, Args...args) { ImGui::TableNextColumn(); ImGui::Text(label); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1); ImGui::Text(format, args...); } static void Section(const char* label) { ImGui::TableNextColumn(); ImGui::TextColored(ImVec4(1, 1, 0, 1), label); ImGui::TableNextColumn(); } static bool InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags = 0) { ImGui::TableNextColumn(); ImGui::Text(label); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1); std::string s("##" + std::string(label)); return ImGui::InputScalar(s.c_str(), data_type, p_data, p_step, p_step_fast, format, flags); } static bool ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0) { ImGui::TableNextColumn(); ImGui::Text(label); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1); return ImGui::ColorEdit3(label, col, flags); } static bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0) { ImGui::TableNextColumn(); ImGui::Text(label); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1); return ImGui::ColorEdit4(label, col, flags); } static void PlotLines(const char* label, float(*getter)(void*, int), void* data, int values_count, int values_offset, const char* overlay = nullptr, float scale_min = FLT_MAX, float scale_max = FLT_MAX) { ImGui::TableNextColumn(); ImGui::Text(label); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1); ImGui::PlotLines("", getter, data, values_count, values_offset, overlay, scale_min, scale_max); } static void End() { ImGui::EndTable(); } } // Added functions to the top level ImGui namespace namespace ImGuiEx { template static void TextCentered(const char* format, Args&&... args) { char buf[1024]; sprintf(buf, format, args...); ImGui::SetCursorPosX((ImGui::GetWindowSize().x - ImGui::CalcTextSize(buf).x) * 0.5f); ImGui::Text(buf); } static void OSGTexture(osg::Texture2D* texture, osg::RenderInfo& renderInfo, unsigned int width = 0, unsigned int height = 0) { // Get the context id const unsigned int contextID = osgEarth::GLUtils::getSharedContextID(*renderInfo.getState()); // Apply the texture texture->apply(*renderInfo.getState()); // Default to the textures size unsigned int w = texture->getTextureWidth(); unsigned int h = texture->getTextureHeight(); // Get the aspect ratio double ar = (double)w / (double)h; // If both width and height are specified use that. if (width != 0 && height != 0) { w = width; h = height; } // If just the width is specified compute the height using the ar else if (width != 0) { w = width; h = (1.0 / ar) * w; } // If just the height is specified compute the width using the ar else if (height != 0) { h = height; w = ar * height; } // Get the TextureObject. osg::Texture::TextureObject* textureObject = texture->getTextureObject(contextID); if (textureObject) { bool flip = (texture->getImage() && texture->getImage()->getOrigin() == osg::Image::TOP_LEFT); ImGui::Image((void*)(intptr_t)textureObject->_id, ImVec2(w, h), ImVec2(0, flip ? 0 : 1), ImVec2(1, flip ? 1 : 0), ImVec4(1, 1, 1, 1), ImVec4(1, 1, 0, 1)); } } static int InputTextCallback(ImGuiInputTextCallbackData* data) { std::string* str = (std::string*)data->UserData; if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) { // Resize string callback IM_ASSERT(data->Buf == str->c_str()); str->resize(data->BufTextLen); data->Buf = (char*)str->c_str(); } return 0; } static bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size) { ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize; return ImGui::InputTextMultiline(label, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, (void*)str); } }