Implicit surface rendering via ray tracing
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1321 lines
42 KiB

/*
* Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2018 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
/// \nodoc (keyword to exclude this file from automatic README.md generation)
#ifndef NV_IMGUI_INCLUDED
#define NV_IMGUI_INCLUDED
#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 1
#include "imgui.h"
#include "imgui_internal.h"
#include <nvpwindow.hpp>
#include "nvmath/nvmath.h"
#include <algorithm>
#include <limits>
#include <array>
#include <climits>
#include <functional>
#include <string>
#include <vector>
enum ImGui_Extra
{
ImGuiPlotType_Area = ImGuiPlotType_Histogram + 1
};
struct GLFWwindow;
namespace ImGuiH {
//////////////////////////////////////////////////////////////////////////
// NVPWindow callbacks
inline bool mouse_pos(int x, int y)
{
auto& io = ImGui::GetIO();
io.AddMousePosEvent((float)x, (float)y);
return io.WantCaptureMouse;
}
inline bool mouse_button(int button, int action)
{
auto& io = ImGui::GetIO();
io.AddMouseButtonEvent(button, action == NVPWindow::BUTTON_PRESS);
return io.WantCaptureMouse;
}
inline bool mouse_wheel(int wheel)
{
auto& io = ImGui::GetIO();
io.AddMouseWheelEvent(0, (float)wheel);
return io.WantCaptureMouse;
}
inline bool key_char(int button)
{
auto& io = ImGui::GetIO();
io.AddInputCharacter(static_cast<unsigned int>(button));
return io.WantCaptureKeyboard;
}
inline ImGuiKey KeyToImGuiKey(int key)
{
switch(key)
{
case NVPWindow::KEY_TAB:
return ImGuiKey_Tab;
case NVPWindow::KEY_LEFT:
return ImGuiKey_LeftArrow;
case NVPWindow::KEY_RIGHT:
return ImGuiKey_RightArrow;
case NVPWindow::KEY_UP:
return ImGuiKey_UpArrow;
case NVPWindow::KEY_DOWN:
return ImGuiKey_DownArrow;
case NVPWindow::KEY_PAGE_UP:
return ImGuiKey_PageUp;
case NVPWindow::KEY_PAGE_DOWN:
return ImGuiKey_PageDown;
case NVPWindow::KEY_HOME:
return ImGuiKey_Home;
case NVPWindow::KEY_END:
return ImGuiKey_End;
case NVPWindow::KEY_INSERT:
return ImGuiKey_Insert;
case NVPWindow::KEY_DELETE:
return ImGuiKey_Delete;
case NVPWindow::KEY_BACKSPACE:
return ImGuiKey_Backspace;
case NVPWindow::KEY_SPACE:
return ImGuiKey_Space;
case NVPWindow::KEY_ENTER:
return ImGuiKey_Enter;
case NVPWindow::KEY_ESCAPE:
return ImGuiKey_Escape;
case NVPWindow::KEY_APOSTROPHE:
return ImGuiKey_Apostrophe;
case NVPWindow::KEY_COMMA:
return ImGuiKey_Comma;
case NVPWindow::KEY_MINUS:
return ImGuiKey_Minus;
case NVPWindow::KEY_PERIOD:
return ImGuiKey_Period;
case NVPWindow::KEY_SLASH:
return ImGuiKey_Slash;
case NVPWindow::KEY_SEMICOLON:
return ImGuiKey_Semicolon;
case NVPWindow::KEY_EQUAL:
return ImGuiKey_Equal;
case NVPWindow::KEY_LEFT_BRACKET:
return ImGuiKey_LeftBracket;
case NVPWindow::KEY_BACKSLASH:
return ImGuiKey_Backslash;
case NVPWindow::KEY_RIGHT_BRACKET:
return ImGuiKey_RightBracket;
case NVPWindow::KEY_GRAVE_ACCENT:
return ImGuiKey_GraveAccent;
case NVPWindow::KEY_CAPS_LOCK:
return ImGuiKey_CapsLock;
case NVPWindow::KEY_SCROLL_LOCK:
return ImGuiKey_ScrollLock;
case NVPWindow::KEY_NUM_LOCK:
return ImGuiKey_NumLock;
case NVPWindow::KEY_PRINT_SCREEN:
return ImGuiKey_PrintScreen;
case NVPWindow::KEY_PAUSE:
return ImGuiKey_Pause;
case NVPWindow::KEY_KP_0:
return ImGuiKey_Keypad0;
case NVPWindow::KEY_KP_1:
return ImGuiKey_Keypad1;
case NVPWindow::KEY_KP_2:
return ImGuiKey_Keypad2;
case NVPWindow::KEY_KP_3:
return ImGuiKey_Keypad3;
case NVPWindow::KEY_KP_4:
return ImGuiKey_Keypad4;
case NVPWindow::KEY_KP_5:
return ImGuiKey_Keypad5;
case NVPWindow::KEY_KP_6:
return ImGuiKey_Keypad6;
case NVPWindow::KEY_KP_7:
return ImGuiKey_Keypad7;
case NVPWindow::KEY_KP_8:
return ImGuiKey_Keypad8;
case NVPWindow::KEY_KP_9:
return ImGuiKey_Keypad9;
case NVPWindow::KEY_KP_DECIMAL:
return ImGuiKey_KeypadDecimal;
case NVPWindow::KEY_KP_DIVIDE:
return ImGuiKey_KeypadDivide;
case NVPWindow::KEY_KP_MULTIPLY:
return ImGuiKey_KeypadMultiply;
case NVPWindow::KEY_KP_SUBTRACT:
return ImGuiKey_KeypadSubtract;
case NVPWindow::KEY_KP_ADD:
return ImGuiKey_KeypadAdd;
case NVPWindow::KEY_KP_ENTER:
return ImGuiKey_KeypadEnter;
case NVPWindow::KEY_KP_EQUAL:
return ImGuiKey_KeypadEqual;
case NVPWindow::KEY_LEFT_SHIFT:
return ImGuiKey_LeftShift;
case NVPWindow::KEY_LEFT_CONTROL:
return ImGuiKey_LeftCtrl;
case NVPWindow::KEY_LEFT_ALT:
return ImGuiKey_LeftAlt;
case NVPWindow::KEY_LEFT_SUPER:
return ImGuiKey_LeftSuper;
case NVPWindow::KEY_RIGHT_SHIFT:
return ImGuiKey_RightShift;
case NVPWindow::KEY_RIGHT_CONTROL:
return ImGuiKey_RightCtrl;
case NVPWindow::KEY_RIGHT_ALT:
return ImGuiKey_RightAlt;
case NVPWindow::KEY_RIGHT_SUPER:
return ImGuiKey_RightSuper;
case NVPWindow::KEY_MENU:
return ImGuiKey_Menu;
case NVPWindow::KEY_0:
return ImGuiKey_0;
case NVPWindow::KEY_1:
return ImGuiKey_1;
case NVPWindow::KEY_2:
return ImGuiKey_2;
case NVPWindow::KEY_3:
return ImGuiKey_3;
case NVPWindow::KEY_4:
return ImGuiKey_4;
case NVPWindow::KEY_5:
return ImGuiKey_5;
case NVPWindow::KEY_6:
return ImGuiKey_6;
case NVPWindow::KEY_7:
return ImGuiKey_7;
case NVPWindow::KEY_8:
return ImGuiKey_8;
case NVPWindow::KEY_9:
return ImGuiKey_9;
case NVPWindow::KEY_A:
return ImGuiKey_A;
case NVPWindow::KEY_B:
return ImGuiKey_B;
case NVPWindow::KEY_C:
return ImGuiKey_C;
case NVPWindow::KEY_D:
return ImGuiKey_D;
case NVPWindow::KEY_E:
return ImGuiKey_E;
case NVPWindow::KEY_F:
return ImGuiKey_F;
case NVPWindow::KEY_G:
return ImGuiKey_G;
case NVPWindow::KEY_H:
return ImGuiKey_H;
case NVPWindow::KEY_I:
return ImGuiKey_I;
case NVPWindow::KEY_J:
return ImGuiKey_J;
case NVPWindow::KEY_K:
return ImGuiKey_K;
case NVPWindow::KEY_L:
return ImGuiKey_L;
case NVPWindow::KEY_M:
return ImGuiKey_M;
case NVPWindow::KEY_N:
return ImGuiKey_N;
case NVPWindow::KEY_O:
return ImGuiKey_O;
case NVPWindow::KEY_P:
return ImGuiKey_P;
case NVPWindow::KEY_Q:
return ImGuiKey_Q;
case NVPWindow::KEY_R:
return ImGuiKey_R;
case NVPWindow::KEY_S:
return ImGuiKey_S;
case NVPWindow::KEY_T:
return ImGuiKey_T;
case NVPWindow::KEY_U:
return ImGuiKey_U;
case NVPWindow::KEY_V:
return ImGuiKey_V;
case NVPWindow::KEY_W:
return ImGuiKey_W;
case NVPWindow::KEY_X:
return ImGuiKey_X;
case NVPWindow::KEY_Y:
return ImGuiKey_Y;
case NVPWindow::KEY_Z:
return ImGuiKey_Z;
case NVPWindow::KEY_F1:
return ImGuiKey_F1;
case NVPWindow::KEY_F2:
return ImGuiKey_F2;
case NVPWindow::KEY_F3:
return ImGuiKey_F3;
case NVPWindow::KEY_F4:
return ImGuiKey_F4;
case NVPWindow::KEY_F5:
return ImGuiKey_F5;
case NVPWindow::KEY_F6:
return ImGuiKey_F6;
case NVPWindow::KEY_F7:
return ImGuiKey_F7;
case NVPWindow::KEY_F8:
return ImGuiKey_F8;
case NVPWindow::KEY_F9:
return ImGuiKey_F9;
case NVPWindow::KEY_F10:
return ImGuiKey_F10;
case NVPWindow::KEY_F11:
return ImGuiKey_F11;
case NVPWindow::KEY_F12:
return ImGuiKey_F12;
default:
return ImGuiKey_None;
}
}
inline bool key_button(int button, int action, int mods)
{
if(button == NVPWindow::KEY_KP_ENTER)
{
button = NVPWindow::KEY_ENTER;
}
auto& io = ImGui::GetIO();
io.AddKeyEvent(ImGuiKey::ImGuiMod_Ctrl, (mods & NVPWindow::KMOD_CONTROL) != 0);
io.AddKeyEvent(ImGuiKey::ImGuiMod_Shift, (mods & NVPWindow::KMOD_SHIFT) != 0);
io.AddKeyEvent(ImGuiKey::ImGuiMod_Alt, (mods & NVPWindow::KMOD_ALT) != 0);
io.AddKeyEvent(ImGuiKey::ImGuiMod_Super, (mods & NVPWindow::KMOD_SUPER) != 0);
ImGuiKey keyIndex = KeyToImGuiKey(button);
io.AddKeyEvent(keyIndex, action == NVPWindow::BUTTON_PRESS);
return io.WantCaptureKeyboard;
}
//////////////////////////////////////////////////////////////////////////
enum FontMode
{
FONT_DEFAULT_SCALED,
FONT_PROPORTIONAL_SCALED,
FONT_MONOSPACED_SCALED,
};
void Init(int width, int height, void* userData, FontMode fontmode = FONT_DEFAULT_SCALED);
void Deinit();
// begin - gl_vk_threaded_cadscene
template <typename T>
bool Clamped(bool changed, T* value, T min, T max)
{
*value = std::max(min, std::min(max, *value));
return changed;
}
inline bool InputIntClamped(const char* label,
unsigned int* v,
int min = INT_MIN,
int max = INT_MAX,
int step = 1,
int step_fast = 100,
ImGuiInputTextFlags flags = 0)
{
return Clamped(ImGui::InputInt(label, (int*)v, step, step_fast, flags), (int*)v, min, max);
}
inline bool InputIntClamped(const char* label, int* v, int min = INT_MIN, int max = INT_MAX, int step = 1, int step_fast = 100, ImGuiInputTextFlags flags = 0)
{
return Clamped(ImGui::InputInt(label, v, step, step_fast, flags), v, min, max);
}
inline bool InputFloatClamped(const char* label,
float* v,
float min = 0.0,
float max = 1.0,
float step = 0.1f,
float step_fast = 1.0f,
const char* fmt = "%.3f",
ImGuiInputTextFlags flags = 0)
{
return Clamped(ImGui::InputFloat(label, v, step, step_fast, fmt, flags), v, min, max);
}
enum ValueType
{
TYPE_INT,
TYPE_FLOAT,
TYPE_BOOL
};
struct Enum
{
union
{
int ivalue;
float fvalue;
bool bvalue;
};
std::string name;
};
bool Combo(const char* label,
size_t numEnums,
const Enum* enums,
void* value,
ImGuiComboFlags flags = 0,
ValueType valueType = TYPE_INT,
bool* valueChanged = NULL);
class Registry
{
private:
struct Entry
{
std::vector<Enum> enums;
ValueType valueType = ValueType::TYPE_INT;
bool valueChanged = false;
};
std::vector<Entry> entries;
public:
const std::vector<Enum>& getEnums(uint32_t type) const { return entries[type].enums; }
void checkboxAdd(uint32_t type, bool value = false)
{
if(type >= entries.size())
{
entries.resize(type + 1ULL);
}
entries[type].enums.push_back({{value}, std::string("Bool")});
entries[type].valueChanged = false;
entries[type].valueType = TYPE_BOOL;
}
void inputIntAdd(uint32_t type, int value = 0)
{
if(type >= entries.size())
{
entries.resize(type + 1ULL);
}
entries[type].enums.push_back({{value}, std::string("iI")});
entries[type].valueChanged = false;
entries[type].valueType = TYPE_INT;
}
void inputFloatAdd(uint32_t type, float value = .0f)
{
if(type >= entries.size())
{
entries.resize(type + 1ULL);
}
Enum e;
e.fvalue = value;
e.name = std::string("iF");
entries[type].enums.push_back(e);
entries[type].valueChanged = false;
entries[type].valueType = TYPE_FLOAT;
}
void enumAdd(uint32_t type, int value, const char* name)
{
if(type >= entries.size())
{
entries.resize(type + 1ULL);
}
entries[type].enums.push_back({{value}, name});
entries[type].valueChanged = false;
entries[type].valueType = TYPE_INT; // the user must be consistent so that he adds only the same type for the same combo !
}
void enumAdd(uint32_t type, float value, const char* name)
{
if(type >= entries.size())
{
entries.resize(type + 1ULL);
}
Enum e;
e.fvalue = value;
e.name = name;
entries[type].enums.push_back(e);
entries[type].valueChanged = false;
entries[type].valueType = TYPE_FLOAT; // the user must be consistent so that he adds only the same type for the same combo !
}
void enumReset(uint32_t type)
{
if(type < entries.size())
{
entries[type].enums.clear();
entries[type].valueChanged = false;
entries[type].valueType = TYPE_INT;
}
}
bool enumCombobox(uint32_t type, const char* label, void* value, ImGuiComboFlags flags = 0, bool* valueChanged = NULL)
{
bool bRes = Combo(label, entries[type].enums.size(), entries[type].enums.data(), value, flags,
entries[type].valueType, &entries[type].valueChanged);
if(valueChanged)
{
*valueChanged = entries[type].valueChanged;
}
return bRes;
}
bool checkbox(uint32_t type, const char* label, void* value, ImGuiComboFlags flags = 0, bool* valueChanged = NULL)
{
bool* pB = &entries[type].enums[0].bvalue;
bool prevValue = *pB;
bool bRes = ImGui::Checkbox(label, pB);
if(prevValue != *pB)
{
entries[type].valueChanged = true;
}
if(valueChanged)
{
*valueChanged = entries[type].valueChanged;
}
if(value)
{
((bool*)value)[0] = pB[0];
}
return bRes;
}
bool inputIntClamped(uint32_t type, const char* label, int* v, int min = INT_MIN, int max = INT_MAX, int step = 1, int step_fast = 100, ImGuiInputTextFlags flags = 0)
{
int* pI = &entries[type].enums[0].ivalue;
int prevValue = *pI;
bool bRes = Clamped(ImGui::InputInt(label, pI, step, step_fast, flags), pI, min, max);
if(prevValue != *pI)
{
entries[type].valueChanged = true;
}
if(v)
{
((int*)v)[0] = pI[0];
}
return bRes;
}
//void inputFloatClamped(uint32_t type, const char* label, float* v, float min = 0.0f, int max = 100.0f, int step = 0.1, int step_fast = 1.0, ImGuiInputTextFlags flags = 0) {
// float *pF = &entries[type].enums[0].fvalue;
// float prevValue = *pF;
// bool bRes = Clamped(ImGui::InputFloat(label, pF, step, step_fast, flags), pF, min, max);
// if (prevValue != *pF)
// entries[type].valueChanged = true;
// if (v)
// ((float*)v)[0] = pF[0];
//}
bool checkValueChange(uint32_t type, bool reset = true)
{
bool changed = entries[type].valueChanged;
if(reset && changed)
{
entries[type].valueChanged = false;
}
return changed;
}
};
//--------------------------------------------------------------------------------------------------
//
// If GLFW has been initialized, returns the DPI scale of the primary monitor. Otherwise, returns 1.
//
float getDPIScale();
inline float dpiScaled(float f)
{
return f * getDPIScale();
}
inline ImVec2 dpiScaled(ImVec2 v)
{
return ImVec2(v.x * getDPIScale(), v.y * getDPIScale());
}
inline ImVec2 dpiScaled(float x, float y)
{
return ImVec2(x * getDPIScale(), y * getDPIScale());
}
//--------------------------------------------------------------------------------------------------
//
// Setting common style across samples
//
void setStyle(bool useLinearColor = false);
//--------------------------------------------------------------------------------------------------
//
// Setting nicer default fonts
//
void setFonts(FontMode fontmode = FONT_PROPORTIONAL_SCALED);
// Display a tooltip for the previous item
void tooltip(const char* description, bool questionMark = false, float timerThreshold = 0.5f);
//--------------------------------------------------------------------------------------------------
// Creating a window panel
// - Panel will be on the left or the right side of the window.
// - It fills the height of the window, and stays on the side it was created.
class Panel /*static*/
{
static ImGuiID dockspaceID;
public:
// Side where the panel will be
enum class Side
{
Left,
Right,
};
// Starting the panel, equivalent to ImGui::Begin for a window. Need ImGui::end()
static void Begin(Side side = Side::Right, float alpha = 0.5f, char* name = nullptr);
// Mirror begin but can use directly End()
static void End() { ImGui::End(); }
// Return the position and size of the central display
static void CentralDimension(ImVec2& pos, ImVec2& size)
{
auto dock_main = ImGui::DockBuilderGetCentralNode(dockspaceID);
if(dock_main)
{
pos = dock_main->Pos;
size = dock_main->Size;
}
}
};
//--------------------------------------------------------------------------------------------------
// GUI control elements which have a 'property page' look.
// - Name on the left, justified on the right
// - Value filling the right side
// - Support for 1..4 components, int, float, bool
// - Tooltip over all param name for the description
// - Default values (reset to default)
// - Min / Max for all value widgets
//
// Controls:
// Slider - Slider with bounded values
// Drag - Infinite values, but min or max values can be set
// Checkbox - Simple [x]
// Selection - Combobox with simple vector of string or fct callback
// Text - Text edit box to enter/modify text
// Color - Color picker
// Button - param + button, or just a button when param name is empty
// Group - Grouping a list of elements (collapsible)
// Info - Simple info text
class Control /*static*/
{
public:
// Influences the rendering of elements.
enum class Flags
{
Normal = 0, // Normal display
Disabled = 1 << 0, // Grayed out
};
struct Style
{
float ctrlPerc = 0.7f; // Percentage the value control takes in the pane
float ctrlMaxSize = 500.f; // Max pixel size control will take
float ctrlMinSize = 50.f; // Minimum size of the control
float dragMinSize = 150.f; // Minimum size to be on one line
};
static Style style;
public:
// Slider - used for bounded values
template <typename TValue, typename TTooltip>
static bool Slider(const std::string& label, // Name of the parameter
TTooltip description, // Tooltip info
TValue* value, // value
const void* default_value = nullptr,
Flags flags = Flags::Normal,
TValue min = TValue(0),
TValue max = TValue(1),
const char* format = nullptr)
{
return show_numeric_control(label, description, value, default_value, flags,
[&] { return show_slider_control<TValue>(value, min, max, format); });
}
// Drag - used for unbound float values. Min and max (or only one) can be specified.
template <typename TValue, typename TTooltip>
static bool Drag(const std::string& label,
TTooltip description,
TValue* value,
const void* default_value = nullptr,
Flags flags = Flags::Normal,
TValue min = std::numeric_limits<TValue>::lowest(),
TValue max = std::numeric_limits<TValue>::max(),
float speed = std::is_integral<TValue>::value ? 0.25f : 0.01f,
const char* format = nullptr)
{
return show_numeric_control(label, description, value, default_value, flags,
[&] { return show_drag_control<TValue>(value, speed, min, max, format); });
}
// Checkbox - to toggle options on and off.
template <typename TTooltip>
static bool Checkbox(const std::string& label, TTooltip description, bool* value, const bool* default_value = nullptr, Flags flags = Flags::Normal);
// Combobox(1) - to select between options.
template <typename Tinteger, typename TTooltip>
static bool Selection(const std::string& label,
TTooltip description,
Tinteger* index,
const void* default_index,
Flags flags,
std::function<const char*(Tinteger)> get_value);
// Combobox(2) - to select between options.
template <typename Tinteger, typename TTooltip>
static bool Selection(const std::string& label,
TTooltip description,
Tinteger* index,
const void* default_index,
Flags flags,
std::vector<std::string> values)
{
return Selection<Tinteger>(label, description, index, default_index, flags,
[&](Tinteger i) { return i < values.size() ? values[i].c_str() : nullptr; });
}
// Text - to input text
template <typename TTooltip>
static bool Text(const std::string& label,
TTooltip description,
char* value_buffer,
size_t value_buffer_size,
const char* default_value,
Flags flags = Flags::Normal);
// Pick - to pick a color.
template <typename TTooltip>
static bool Color(const std::string& label,
TTooltip description,
float* value_float3,
const float* default_value_float3 = nullptr,
Flags flags = Flags::Normal);
// Button - to start an action or for navigation.
template <typename TTooltip>
static bool button(const std::string& label, const std::string& button_text, TTooltip description, Flags flags = Flags::Normal);
// Group - Create a collapsible group that can nest other elements.
template <typename TReturn>
static TReturn Group(const std::string& name, bool expanded_by_default, std::function<TReturn(void)> show_content);
// Info - Shows an text label only without interaction possibility.
template <typename TTooltip>
static void Info(const std::string& label, TTooltip description, const char* info_text, Flags flags = Flags::Normal);
template <typename TTooltip>
static void Info(const std::string& label, TTooltip description, const std::string& info_text, Flags flags = Flags::Normal)
{
Info(label, description, info_text.c_str(), flags);
}
template <typename TTooltip>
static bool Custom(const std::string& label, TTooltip description, std::function<bool()> show_content, Flags flags = Flags::Normal);
private:
template <typename TTooltip>
static void show_property_label(const std::string& text, TTooltip description, Flags flags);
template <typename TValue>
static bool show_slider_control(TValue* value, TValue& min, TValue& max, const char* format);
template <typename TValue>
static bool show_drag_control(TValue* value, float speed, TValue& min, TValue& max, const char* format);
template <typename TValue, typename TTooltip>
static bool show_numeric_control(const std::string& label,
TTooltip description,
TValue* value,
const void* default_value,
Flags flags,
std::function<bool(void)> show_numeric_control);
template <typename TScalar, ImGuiDataType type, uint8_t dim>
static bool show_drag_control_scalar(TScalar* value, float speed, TScalar* min, TScalar* max, const char* format);
template <typename TScalar, ImGuiDataType type, uint8_t dim>
static bool show_slider_control_scalar(TScalar* value, TScalar* min, TScalar* max, const char* format);
};
//////////////////////////////////////////////////////////////////////////
// Template IMPLEMENTATION
//
//
// Displaying the parameter name
// - When string ends with \n, the parameters will be on next line instead of on the right
//
template <typename TTooltip>
void ImGuiH::Control::show_property_label(const std::string& text, TTooltip description, Flags flags)
{
if(flags == Flags::Disabled)
{
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);
}
if(text.back() == '\n')
{
ImGui::TextWrapped("%s", text.c_str());
}
else
{
// Indenting the text to be right justified
float current_indent = ImGui::GetCursorPos().x;
const ImGuiStyle& imstyle = ImGui::GetStyle();
const ImGuiWindow* window = ImGui::GetCurrentWindow();
float control_width = std::min((ImGui::GetWindowWidth() - imstyle.IndentSpacing) * style.ctrlPerc, style.ctrlMaxSize);
control_width -= window->ScrollbarSizes.x;
control_width = std::max(control_width, style.ctrlMinSize);
float available_width = ImGui::GetContentRegionAvail().x;
float avaiable_text_width = available_width - control_width - imstyle.ItemInnerSpacing.x;
ImVec2 text_size = ImGui::CalcTextSize(text.c_str(), text.c_str() + text.size(), false, avaiable_text_width);
float indent = current_indent + available_width - control_width - text_size.x - imstyle.ItemInnerSpacing.x;
ImGui::AlignTextToFramePadding();
ImGui::NewLine();
ImGui::SameLine(indent);
ImGui::PushTextWrapPos(indent + avaiable_text_width);
ImGui::TextWrapped("%s", text.c_str());
ImGui::PopTextWrapPos();
ImGui::SameLine();
}
if(flags == Flags::Disabled)
ImGui::PopStyleColor();
ImGuiH::tooltip(description, false, 0.0f);
}
//
// Add the value controls (sliders, drag)
// - handle default value
//
template <typename TValue, typename TTooltip>
bool ImGuiH::Control::show_numeric_control(const std::string& label,
TTooltip description,
TValue* value,
const void* default_value,
Flags flags,
std::function<bool(void)> show_numeric_control)
{
ImGui::PushID(value);
show_property_label(label, description, flags);
if(flags == Flags::Disabled)
{
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
bool changed = show_numeric_control();
if(default_value && ImGui::BeginPopupContextItem("item context menu"))
{
if(ImGui::Selectable("set default"))
{
changed = *value != *(reinterpret_cast<const TValue*>(default_value));
*value = *(reinterpret_cast<const TValue*>(default_value));
}
ImGui::EndPopup();
}
if(flags == Flags::Disabled)
{
ImGui::PopItemFlag();
ImGui::PopStyleVar();
}
ImGui::PopID();
return changed;
}
//
//
template <typename TTooltip>
void ImGuiH::Control::Info(const std::string& label, TTooltip description, const char* info_text, Flags flags)
{
ImGui::PushID(&info_text);
if(label.empty() == false)
show_property_label(label, description, flags);
if(flags == Flags::Disabled)
{
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::TextWrapped("%s", info_text);
if(flags == Flags::Disabled)
{
ImGui::PopItemFlag();
ImGui::PopStyleVar();
}
if(label.empty())
ImGuiH::tooltip(description, false, 0.0f);
ImGui::PopID();
}
template <typename TReturn>
TReturn ImGuiH::Control::Group(const std::string& name, bool expanded_by_default, std::function<TReturn(void)> show_content)
{
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec4 color = ImGui::GetStyle().Colors[ImGuiCol_Text];
color.x *= 0.6f;
color.y *= 0.6f;
color.z *= 0.6f;
const ImU32 draw_color = ImColor(color);
const float draw_line_width = 1.0f;
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
TReturn ret = static_cast<TReturn>(0);
const ImVec2 start_top = ImGui::GetCursorScreenPos();
ImGui::PushID(&show_content);
if(ImGui::TreeNodeEx(name.c_str(), expanded_by_default ? ImGuiTreeNodeFlags_DefaultOpen : ImGuiTreeNodeFlags_None))
{
ImGui::PopStyleColor();
ImGui::PopStyleColor();
const ImVec2 start = ImGui::GetCursorScreenPos();
ret = show_content();
const ImVec2 end = ImGui::GetCursorScreenPos();
draw_list->AddLine(ImVec2(start.x - draw_line_width * 0.5f, (start.y + start_top.y) * 0.5f),
ImVec2(end.x - draw_line_width * 0.5f, end.y), draw_color, draw_line_width);
ImGui::TreePop();
}
else
{
ImGui::PopStyleColor();
ImGui::PopStyleColor();
}
ImGui::PopID();
ImGui::Spacing();
return ret;
}
template <typename TTooltip>
bool ImGuiH::Control::button(const std::string& label, const std::string& button_text, TTooltip description, Flags flags /*= Flags::Normal*/)
{
ImGui::PushID(&description);
// show the left side label with tool tips
if(!label.empty())
show_property_label(label, description, flags);
bool label_fits = (ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(button_text.c_str()).x
- ImGui::GetStyle().ItemInnerSpacing.x * 2.0f)
> 0.0f;
if(flags == Flags::Disabled)
{
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
}
bool pressed = ImGui::Button((label.empty() || label_fits ? button_text.c_str() : "..."),
ImVec2(ImGui::GetContentRegionAvail().x, 0.0f));
if(flags == Flags::Disabled)
{
ImGui::PopItemFlag();
ImGui::PopStyleVar();
}
// show the tool tip when hovering over the button in case there is no left side label
if(label.empty())
ImGuiH::tooltip(description, false, 0.0f);
ImGui::PopID();
return pressed;
}
template <typename TTooltip>
bool ImGuiH::Control::Color(const std::string& label, TTooltip description, float* value_float3, const float* default_value_float3, Flags flags)
{
ImGui::PushID(value_float3);
show_property_label(label, description, flags);
if(flags == Flags::Disabled)
{
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
bool changed = ImGui::ColorEdit3("##hidden", &value_float3[0],
ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_Float | ImGuiColorEditFlags_NoAlpha);
if(default_value_float3 && ImGui::BeginPopupContextItem("item context menu"))
{
if(ImGui::Selectable("set default"))
{
changed = value_float3[0] != default_value_float3[0] || value_float3[1] != default_value_float3[1]
|| value_float3[2] != default_value_float3[2];
value_float3[0] = default_value_float3[0];
value_float3[1] = default_value_float3[1];
value_float3[2] = default_value_float3[2];
}
ImGui::EndPopup();
}
if(flags == Flags::Disabled)
{
ImGui::PopItemFlag();
ImGui::PopStyleVar();
}
ImGui::PopID();
return changed;
}
// Combo-box to select between options.
template <typename Tinteger, typename TTooltip>
bool ImGuiH::Control::Selection(const std::string& label,
TTooltip description,
Tinteger* index,
const void* default_index,
Flags flags,
std::function<const char*(Tinteger)> get_value)
{
return show_numeric_control(label, description, index, default_index, flags, [&] {
bool valid = false;
bool changed = false;
if(ImGui::BeginCombo("##hidden", get_value(*index)))
{
Tinteger i = 0;
while(true)
{
const char* option = get_value(i);
if(!option)
break;
valid |= (i == *index); // check if current selection is a valid option
if(ImGui::Selectable(option, i == *index))
{
*index = i;
changed = true;
valid = true;
}
i++;
}
ImGui::EndCombo();
if(!valid && default_index)
{
*index = *reinterpret_cast<const Tinteger*>(default_index);
changed = true;
}
}
return changed;
});
}
template <typename TTooltip>
bool ImGuiH::Control::Text(const std::string& label, TTooltip description, char* value_buffer, size_t value_buffer_size, const char* default_value, Flags flags)
{
//assuming that this is unique, but constant from frame to frame
int id = *static_cast<int*>((void*)&label);
id ^= *static_cast<int*>((void*)&description);
id ^= *static_cast<int*>((void*)&default_value);
ImGui::PushID(id);
show_property_label(label, description, flags);
if(flags == Flags::Disabled)
{
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
bool changed = ImGui::InputText("##hidden", value_buffer, value_buffer_size, ImGuiInputTextFlags_EnterReturnsTrue);
if(default_value && ImGui::BeginPopupContextItem("item context menu"))
{
if(ImGui::Selectable("set default"))
{
changed = strcmp(value_buffer, default_value) != 0;
memset(value_buffer, '\0', value_buffer_size);
strncpy(value_buffer, default_value, std::min(value_buffer_size, strlen(default_value)));
}
ImGui::EndPopup();
}
if(flags == Flags::Disabled)
{
ImGui::PopItemFlag();
ImGui::PopStyleVar();
}
ImGui::PopID();
return changed;
}
template <typename TTooltip>
bool ImGuiH::Control::Checkbox(const std::string& label, TTooltip description, bool* value, const bool* default_value /*= nullptr*/, Flags flags /*= Flags::Normal*/)
{
ImGui::PushID(value);
show_property_label(label, description, flags);
if(flags == Flags::Disabled)
{
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
bool changed = ImGui::Checkbox("##hidden", value);
if(default_value && ImGui::BeginPopupContextItem("item context menu"))
{
if(ImGui::Selectable("set default"))
{
changed = *value != *default_value;
*value = *default_value;
}
ImGui::EndPopup();
}
if(flags == Flags::Disabled)
{
ImGui::PopItemFlag();
ImGui::PopStyleVar();
}
ImGui::PopID();
return changed;
}
template <typename TScalar, ImGuiDataType type, uint8_t dim>
bool Control::show_drag_control_scalar(TScalar* value, float speed, TScalar* min, TScalar* max, const char* format)
{
static const char* visible_labels[] = {"x:", "y:", "z:", "w:"};
if(ImGui::GetContentRegionAvail().x > style.dragMinSize)
return ImGui::DragScalarN("##hidden", type, &value[0], dim, speed, &min[0], &max[0], format);
if(dim == 1)
return ImGui::DragScalar("##hidden", type, &value[0], speed, &min[0], &max[0], format);
float indent = ImGui::GetCursorPos().x;
bool changed = false;
for(uint8_t c = 0; c < dim; ++c)
{
ImGui::PushID(c);
if(c > 0)
{
ImGui::NewLine();
ImGui::SameLine(indent);
}
ImGui::Text("%s", visible_labels[c]);
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
changed |= ImGui::DragScalar("##hidden", type, &value[c], speed, &min[c], &max[c], format);
ImGui::PopID();
}
return changed;
}
template <typename TScalar, ImGuiDataType type, uint8_t dim>
bool Control::show_slider_control_scalar(TScalar* value, TScalar* min, TScalar* max, const char* format)
{
static const char* visible_labels[] = {"x:", "y:", "z:", "w:"};
if(dim == 1)
return ImGui::SliderScalar("##hidden", type, &value[0], &min[0], &max[0], format);
float indent = ImGui::GetCursorPos().x;
bool changed = false;
for(uint8_t c = 0; c < dim; ++c)
{
ImGui::PushID(c);
if(c > 0)
{
ImGui::NewLine();
ImGui::SameLine(indent);
}
ImGui::Text("%s", visible_labels[c]);
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
changed |= ImGui::SliderScalar("##hidden", type, &value[c], &min[c], &max[c], format);
ImGui::PopID();
}
return changed;
}
template <typename TTooltip>
bool ImGuiH::Control::Custom(const std::string& label, TTooltip description, std::function<bool()> show_content, Flags flags /*= Flags::Normal*/)
{
//ImGui::PushID(value);
show_property_label(label, description, flags);
if(flags == Flags::Disabled)
{
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
bool changed = show_content();
if(flags == Flags::Disabled)
{
ImGui::PopItemFlag();
ImGui::PopStyleVar();
}
// ImGui::PopID();
return changed;
}
//--------------------------------------------------------------------------------------------------
// This is a helper to create a nice property editor with ImGui, where the name of the property
// is on the left, while all values are on the right.
//
// To use:
// - Call PropertyEditor::Begin() to start the section of the editor
// - For each entry, use the same ImGui property in the lambda function
// Ex: PropertyEditor::Entry("My Prop", [&](){return ImGui::DragFloat(...);});
// - An extra argument can be added, which will display the string as Tooltip
// - Close the property by calling PropertyEditor::End()
//
struct PropertyEditor
{
// Beginning the Property Editor
static void begin(ImGuiTableFlags flag = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable)
{
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2));
const bool table_valid = ImGui::BeginTable("split", 2, flag);
assert(table_valid);
}
// Generic entry, the lambda function should return true if the widget changed
static bool entry(const std::string& property_name, const std::function<bool()>& content_fct, const std::string& tooltip = {})
{
ImGui::PushID(property_name.c_str());
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("%s", property_name.c_str());
if(!tooltip.empty())
ImGuiH::tooltip(tooltip.c_str(), false, 0);
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(-FLT_MIN);
bool result = content_fct();
if(!tooltip.empty())
ImGuiH::tooltip(tooltip.c_str());
ImGui::PopID();
return result; // returning if the widget changed
}
// Text specialization
static void entry(const std::string& property_name, const std::string& value)
{
entry(property_name, [&] {
ImGui::Text("%s", value.c_str());
return false; // dummy, no change
});
}
static bool treeNode(const std::string& name)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
return ImGui::TreeNodeEx(name.c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
}
static void treePop() { ImGui::TreePop(); }
// Ending the Editor
static void end()
{
ImGui::EndTable();
ImGui::PopStyleVar();
}
};
bool azimuthElevationSliders(nvmath::vec3f& direction, bool negative = true, bool yIsUp = true);
// This allow to use the mouse wheel over a widget and change the "data" value by and
// increment. The value can be clamp if min_val != max_val.
// Ex. ImGui::Combo("Value", &combo_item, ...);
// ImGuiH::hoverScrolling(combo_item, 0, 5); // Scroll the item of the combo
template <typename T>
bool hoverScrolling(T& data, T min_val = T(0), T max_val = T(0), float inc = 1.0F)
{
ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY);
if(ImGui::IsItemHovered() && ImGui::GetIO().MouseWheel != 0.0F)
{
data += T(ImGui::GetIO().MouseWheel * inc);
if(min_val != max_val)
data = std::max(std::min(data, max_val), min_val);
return true;
}
return false;
}
} // namespace ImGuiH
namespace ImGui {
struct ImPlotMulti
{
ImGuiPlotType plot_type{ImGuiPlotType_Lines};
const char* name{nullptr};
ImColor color{0xFFFFFFFF};
float thickness{1.0};
const float* data{nullptr};
int values_count{0};
int values_offset{0};
float scale_min{FLT_MIN};
float scale_max{FLT_MAX};
};
void PlotMultiEx(const char* label, int num_datas, ImPlotMulti* datas, const char* overlay_text, ImVec2 frame_size);
} // namespace ImGui
#endif