Commit 9b27bebd authored by Johannes Braun's avatar Johannes Braun
Browse files

Improved GUI by... like a lot (Tabs)

parent 51e164b6
......@@ -10,12 +10,12 @@
<item name="reflection" value="3"/>
<item name="transmission" value="3"/>
<item name="emission" value="1"/>
<item name="accuracy" value="0.4"/>
<item name="accuracy" value="0.04"/>
<item name="shadow" value="0.04"/>
<item name="distance" value="12"/>
<item name="distance" value="25"/>
</group>
<item name="clamp-direct" value="8"/>
<item name="clamp-indirect" value="8"/>
</pathtracer>
\ No newline at end of file
</pathtracer>
#include <array>
#include <imgui/imgui_glfw.h>
#include <util/files.h>
......@@ -31,7 +33,7 @@ const fs::path engine_settings_path = files::asset("/preferences/default.xml");
const fs::path pathtracer_settings_path = files::asset("/preferences/pathtracer_default.xml");
const fs::path skybox_files_path = files::asset("/textures/ryfjallet/");
const fs::path env_audio_path = files::asset("/audio/env.wav");
const fs::path startup_scene_path = files::asset("/meshes/scenery/House.dae");
const fs::path startup_scene_path = files::asset("/meshes/scenery/testscene.dae");
// My little all-enclosing pathtracer pointer
std::unique_ptr<raytrace::Pathtracer> pathtracer = nullptr;
......@@ -146,178 +148,198 @@ void loadScene(const fs::path& path, float scale)
void drawDebugGui()
{
// Debug Window
ImGui::Begin("Debug", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Begin("Settings");
ImGui::Title("General Settings");
// Render Mode
ImGui::BeginNamedGroup("Render Mode");
int vsync = int(core::state::window->getVSync());
if (ImGui::Combo("VSync", &vsync, { "None", "60 FPS", "30 FPS", "20 FPS" }))
core::state::window->setVSync(static_cast<VSync>(vsync));
ImGui::Title("Render Mode");
std::array<std::string, 5> nms{ "Wireframe", "Raw", "GBuffer", "Pathtracing", "Controls" };
static int n_current = 0;
if (ImGui::Tabs(nms, n_current) && n_current < 4)
{
ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth());
int render_state = static_cast<int>(render_mode);
if (ImGui::Combo("", &render_state, { "OpenGL Wireframe", "OpenGL Raw Color", "OpenGL GBuffer", "Pathtracing" }))
render_mode = static_cast<RenderMode>(render_state);
ImGui::PopItemWidth();
render_mode = static_cast<RenderMode>(n_current);
}
ImGui::EndNamedGroup();
if (pathtracer->collector()) {
if (n_current == 3)
{
if (pathtracer->collector()) {
ImGui::Spacing();
ImGui::Spacing();
ImGui::Title("Save Render as...");
if (ImGui::FeatureButton("32bit HDR", ImVec2(ImGui::GetContentRegionAvailWidth() / 2, 0)))
pathtracer->saveRender(files::asset("/screenshots/render.hdr"));
ImGui::Title("Scene Settings");
ImGui::TextWrapped("By default, a transformation or mesh update will trigger a pathtracer sample count reset. If you want to limit this to explicit settings changes and camera movements, uncheck the following option.");
bool collector_active = pathtracer->collector()->isActive();
if (ImGui::Checkbox("Reset on scene update", &collector_active))
pathtracer->collector()->setActive(collector_active);
ImGui::Columns();
ImGui::Spacing();
ImGui::SameLine();
if (ImGui::CollapsingHeader("Sampling and Performance"))
{
ImGui::BeginNamedGroup("Ray Generator");
{
int raygenstate = static_cast<int>(pathtracer->getGenerator());
ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth());
if (ImGui::Combo("", &raygenstate, { "Trace [default]", "OpenGL GBuffer" })) {
pathtracer->setGenerator(static_cast<raytrace::RayGeneratorType>(raygenstate));
}
ImGui::PopItemWidth();
}
ImGui::EndNamedGroup();
if (ImGui::FeatureButton("8bit PNG", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)))
pathtracer->saveRender(files::asset("/screenshots/render.png"));
ImGui::Spacing();
ImGui::BeginNamedGroup("Samples");
std::array<std::string, 3> names{ "Scene Settings", "Sampling and Performance" , "Linespace Thresholds" };
static int curr = 0;
ImGui::VTabs(names, curr);
switch (curr)
{
case 0:
{
ImGui::TextWrapped("By default, a transformation or mesh update will trigger a pathtracer sample count reset. If you want to limit this to explicit settings changes and camera movements, uncheck the following option.");
bool collector_active = pathtracer->collector()->isActive();
if (ImGui::Checkbox("Reset on scene update", &collector_active))
pathtracer->collector()->setActive(collector_active);
}
break;
case 1:
{
ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth());
ImGui::ProgressBar(pathtracer->currentSample() / static_cast<float>(pathtracer->getMaxSamples()), ImVec2(-1, 0),
("Current: " + std::to_string(pathtracer->currentSample()) + " of " + std::to_string(pathtracer->getMaxSamples())).c_str());
ImGui::PopItemWidth();
ImGui::Checkbox("Print render times", &pathtracer->debug.print_render_times);
ImGui::Spacing();
int max_samples = pathtracer->getMaxSamples();
if (ImGui::DragInt("Maximum", &max_samples, 10, 1, 50000))
pathtracer->setMaxSamples(max_samples);
int spfr = pathtracer->getSamplesPerFrame();
if (ImGui::DragInt("per frame", &spfr, 0.1f, 1, 10))
pathtracer->setSamplesPerFrame(spfr);
}
ImGui::EndNamedGroup();
ImGui::BeginNamedGroup("Ray Generator");
{
int raygenstate = static_cast<int>(pathtracer->getGenerator());
ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth());
if (ImGui::Combo("", &raygenstate, { "Trace [default]", "OpenGL GBuffer" })) {
pathtracer->setGenerator(static_cast<raytrace::RayGeneratorType>(raygenstate));
}
ImGui::PopItemWidth();
}
ImGui::EndNamedGroup();
ImGui::Spacing();
ImGui::Spacing();
ImGui::BeginNamedGroup("Bounces");
ImGui::TextWrapped("The global bounce limit clamps down all effect-dependant bounce limits to a unified maximum value.");
{
int bounces = pathtracer->getBounces();
if (ImGui::DragInt("Global Limit", &bounces, 0.1f, 0, 15))
pathtracer->setBounces(bounces);
ImGui::BeginNamedGroup("Samples");
{
ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth());
ImGui::ProgressBar(pathtracer->currentSample() / static_cast<float>(pathtracer->getMaxSamples()), ImVec2(-1, 0),
("Current: " + std::to_string(pathtracer->currentSample()) + " of " + std::to_string(pathtracer->getMaxSamples())).c_str());
ImGui::PopItemWidth();
ImGui::Spacing();
int max_samples = pathtracer->getMaxSamples();
if (ImGui::DragInt("Maximum", &max_samples, 10, 1, 50000))
pathtracer->setMaxSamples(max_samples);
int spfr = pathtracer->getSamplesPerFrame();
if (ImGui::DragInt("per frame", &spfr, 0.1f, 1, 10))
pathtracer->setSamplesPerFrame(spfr);
}
ImGui::EndNamedGroup();
ImGui::Spacing();
if (ImGui::TreeNode("Bounce Thresholds"))
ImGui::BeginNamedGroup("Bounces");
ImGui::TextWrapped("The global bounce limit clamps down all effect-dependant bounce limits to a unified maximum value.");
{
// Let's keep this function locally near to the only place it's being used.
static auto effectBounces = [](const std::string &label, raytrace::EffectType effect)
{
int val = pathtracer->getEffectBounces(effect);
if (ImGui::DragInt(label.c_str(), &val, 0.1f, 0, 255))
pathtracer->setEffectBounces(effect, static_cast<uint8_t>(val));
};
int bounces = pathtracer->getBounces();
if (ImGui::DragInt("Global Limit", &bounces, 0.1f, 0, 15))
pathtracer->setBounces(bounces);
effectBounces("Diffusion", raytrace::EffectType::eDiffusion);
effectBounces("Reflection", raytrace::EffectType::eReflection);
effectBounces("Transmission", raytrace::EffectType::eTransmission);
effectBounces("Emission", raytrace::EffectType::eEmission);
ImGui::TreePop();
ImGui::Spacing();
if (ImGui::TreeNode("Bounce Thresholds"))
{
// Let's keep this function locally near to the only place it's being used.
static auto effectBounces = [](const std::string &label, raytrace::EffectType effect)
{
int val = pathtracer->getEffectBounces(effect);
if (ImGui::DragInt(label.c_str(), &val, 0.1f, 0, 255))
pathtracer->setEffectBounces(effect, static_cast<uint8_t>(val));
};
effectBounces("Diffusion", raytrace::EffectType::eDiffusion);
effectBounces("Reflection", raytrace::EffectType::eReflection);
effectBounces("Transmission", raytrace::EffectType::eTransmission);
effectBounces("Emission", raytrace::EffectType::eEmission);
ImGui::TreePop();
}
}
}
ImGui::EndNamedGroup();
ImGui::EndNamedGroup();
ImGui::TextWrapped("The direct clamp setting will lead to the primary ray result color being clamped down to a set maximum. That means each color component will be clamped. The same applies to the \"clamp indirect\" setting for non-primary bounces.");
{
float clamp_direct = pathtracer->getClampDirect();
if (ImGui::DragFloat("Clamp Direct", &clamp_direct, 0.1f, 0.0f, 100.f))
pathtracer->setClampDirect(clamp_direct);
ImGui::TextWrapped("The direct clamp setting will lead to the primary ray result color being clamped down to a set maximum. That means each color component will be clamped. The same applies to the \"clamp indirect\" setting for non-primary bounces.");
{
float clamp_direct = pathtracer->getClampDirect();
if (ImGui::DragFloat("Clamp Direct", &clamp_direct, 0.1f, 0.0f, 100.f))
pathtracer->setClampDirect(clamp_direct);
float clamp_indirect = pathtracer->getClampIndirect();
if (ImGui::DragFloat("Clamp Indirect", &clamp_indirect, 0.1f, 0.0f, 100.f))
pathtracer->setClampIndirect(clamp_indirect);
float clamp_indirect = pathtracer->getClampIndirect();
if (ImGui::DragFloat("Clamp Indirect", &clamp_indirect, 0.1f, 0.0f, 100.f))
pathtracer->setClampIndirect(clamp_indirect);
}
}
}
if (ImGui::CollapsingHeader("Linespace Thresholds"))
{
ImGui::Title("PDF Thresholds");
ImGui::TextWrapped("The following threshold values will determine as of which accumulated PDF value the Line Space should be used instead of the BVH.");
break;
case 2:
{
float acc = pathtracer->getAccuracyThreshold();
if (ImGui::DragFloat("Direct", &acc, 0.001f, 0.0f, 1.f))
pathtracer->setAccuracyThreshold(acc);
ImGui::Title("PDF Thresholds");
ImGui::TextWrapped("The following threshold values will determine as of which accumulated PDF value the Line Space should be used instead of the BVH.");
{
float acc = pathtracer->getAccuracyThreshold();
if (ImGui::DragFloat("Direct", &acc, 0.001f, 0.0f, 1.f))
pathtracer->setAccuracyThreshold(acc);
float shd = pathtracer->getShadowThreshold();
if (ImGui::DragFloat("Shadow", &shd, 0.001f, 0.0f, 1.f))
pathtracer->setShadowThreshold(shd);
}
float shd = pathtracer->getShadowThreshold();
if (ImGui::DragFloat("Shadow", &shd, 0.001f, 0.0f, 1.f))
pathtracer->setShadowThreshold(shd);
}
ImGui::Title("Distance Threshold");
ImGui::TextWrapped("Works like a level of detail slider. This is the maximum path length with which the BVH should be used.");
{
float dt = pathtracer->getDistanceThreshold();
if (ImGui::DragFloat("Distance", &dt, 0.1f, 0.0f, 10000.f))
pathtracer->setDistanceThreshold(dt);
}
ImGui::Title("Distance Threshold");
ImGui::TextWrapped("Works like a level of detail slider. This is the maximum path length with which the BVH should be used.");
{
float dt = pathtracer->getDistanceThreshold();
if (ImGui::DragFloat("Distance", &dt, 0.1f, 0.0f, 10000.f))
pathtracer->setDistanceThreshold(dt);
}
ImGui::Title("Bounce Thresholds");
ImGui::TextWrapped("The following threshold values will determine as of which bounce the Line Space should be used instead of the BVH. This can vary for every kind of effect.");
{
// Let's keep this function locally near to the only place it's being used.
static auto bounceControl = [](const std::string &label, raytrace::EffectType effect)
ImGui::Title("Bounce Thresholds");
ImGui::TextWrapped("The following threshold values will determine as of which bounce the Line Space should be used instead of the BVH. This can vary for every kind of effect.");
{
int val = pathtracer->getLinespaceBounceThreshold(effect);
if (ImGui::DragInt(label.c_str(), &val, 0.1f, 0, pathtracer->getEffectBounces(effect)))
pathtracer->setLinespaceBounceThresholds(effect, static_cast<uint8_t>(val));
};
bounceControl("Diffusion", raytrace::EffectType::eDiffusion);
bounceControl("Reflection", raytrace::EffectType::eReflection);
bounceControl("Transmission", raytrace::EffectType::eTransmission);
bounceControl("Emission", raytrace::EffectType::eEmission);
// Let's keep this function locally near to the only place it's being used.
static auto bounceControl = [](const std::string &label, raytrace::EffectType effect)
{
int val = pathtracer->getLinespaceBounceThreshold(effect);
if (ImGui::DragInt(label.c_str(), &val, 0.1f, 0, pathtracer->getEffectBounces(effect)))
pathtracer->setLinespaceBounceThresholds(effect, static_cast<uint8_t>(val));
};
bounceControl("Diffusion", raytrace::EffectType::eDiffusion);
bounceControl("Reflection", raytrace::EffectType::eReflection);
bounceControl("Transmission", raytrace::EffectType::eTransmission);
bounceControl("Emission", raytrace::EffectType::eEmission);
}
}
break;
default: break;
}
ImGui::EndTabs();
}
else if (ImGui::FeatureButton("Initialize Pathtracer", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)))
{
pathtracer->reload(core::state::graph_root);
}
ImGui::Spacing();
ImGui::Title("Save Render as...");
if (ImGui::FeatureButton("32bit HDR", ImVec2(ImGui::GetContentRegionAvailWidth() / 2, 0)))
pathtracer->saveRender(files::asset("/screenshots/render.hdr"));
ImGui::SameLine();
if (ImGui::FeatureButton("8bit PNG", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)))
pathtracer->saveRender(files::asset("/screenshots/render.png"));
}
else if (ImGui::FeatureButton("Initialize Pathtracer", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)))
else if (n_current == 4)
{
pathtracer->reload(core::state::graph_root);
ImGui::Title("Controls");
ImGui::TitledDescription("Right Click", "Grab cursor, enables looking around. Right click again to release it.");
ImGui::TitledDescription("W/S", "Fly along the looking direction.");
ImGui::TitledDescription("A/D", "Fly along the horizontal perpendiculat to the looking direction.");
ImGui::TitledDescription("E", "Fly towards the relative up-direction.");
ImGui::TitledDescription("Q", "Fly towards the relative down-direction.");
ImGui::TitledDescription("1, 2, 3", "Toggle rendering mode.");
}
else
{
ImGui::Text("Nothing to set up...");
}
ImGui::Separator();
ImGui::Title("General Settings");
int vsync = int(core::state::window->getVSync());
if (ImGui::Combo("VSync", &vsync, { "None", "60 FPS", "30 FPS", "20 FPS" }))
core::state::window->setVSync(static_cast<VSync>(vsync));
ImGui::Separator();
ImGui::Title("Controls");
ImGui::TitledDescription("Right Click", "Grab cursor, enables looking around. Right click again to release it.");
ImGui::TitledDescription("W/S", "Fly along the looking direction.");
ImGui::TitledDescription("A/D", "Fly along the horizontal perpendiculat to the looking direction.");
ImGui::TitledDescription("E", "Fly towards the relative up-direction.");
ImGui::TitledDescription("Q", "Fly towards the relative down-direction.");
ImGui::TitledDescription("1, 2, 3", "Toggle rendering mode.");
ImGui::EndTabs();
ImGui::End();
......
......@@ -29,7 +29,7 @@ namespace glare
std::shared_ptr<T> getComponent() const;
template<typename T>
std::vector<std::shared_ptr<T>> getComponents() const;
std::vector<std::shared_ptr<TComponent>> getComponents() const;
void iterateComponentsStable(std::function<void(TComponent& component)> loop);
......@@ -107,16 +107,16 @@ namespace glare
template <typename TNode, typename TComponent>
template<typename T>
std::vector<std::shared_ptr<T>> ComponentContainer<TNode, TComponent>::getComponents() const
std::vector<std::shared_ptr<TComponent>> ComponentContainer<TNode, TComponent>::getComponents() const
{
if (m_components.size() == 0 || m_components.count(typeid(T).name()) == 0)
{
return std::vector<std::vector<std::shared_ptr<T>>>();
return std::vector<std::shared_ptr<TComponent>>();
}
else
{
const auto& components = m_components.at(typeid(T).name());
return std::vector<std::vector<std::shared_ptr<T>>>(components.begin(), components.end());
//const auto& components = m_components.at(typeid(T).name());
return m_components.at(typeid(T).name());
}
}
}
......
......@@ -25,33 +25,46 @@ namespace glare
void Material::gui()
{
ImGui::PushID(m_name.c_str());
ImGui::Title("Material: ");
ImGui::SameLine();
ImGui::Text(m_name.c_str());
const static auto colorOption = [](const std::string &name, MaterialParameter3f &param)
ImGui::SameLine();
if (ImGui::Button("Edit...", ImVec2(ImGui::GetContentRegionAvailWidth(), 0)))
{
glm::vec4 val = glm::vec4(param.value, 1);
ImGui::ColorButton(*reinterpret_cast<ImVec4*>(&val));
param.value = val.xyz;
ImGui::SameLine();
return ImGui::DragFloat3(name.c_str(), reinterpret_cast<float*>(&param.value), 0.01f, 0.f, 1.f);
};
const static auto floatOption = [](const std::string &name, MaterialParameter1f &param)
ImGui::OpenPopup("Material_parameter_popup");
}
if (ImGui::BeginPopup("Material_parameter_popup"))
{
return ImGui::DragFloat(name.c_str(), &param.value, 0.001f, 0.f, 1.f);
};
bool changed = false;
changed |= colorOption("Base Color", base);
ImGui::Spacing();
changed |= floatOption("Roughness", roughness);
changed |= floatOption("Metallic", metallic);
changed |= floatOption("Transmissive", transmissive);
changed |= ImGui::DragFloat("Emissive", &emission.value, 0.001f, 0.f, 1000.f);
ImGui::Spacing();
changed |= ImGui::DragFloat("IOR", &index_of_refraction, 0.001f, 0.1f, 10.f);
if (changed)
messaging::Handler::getInstance().submit(tags::material, 1);
const static auto colorOption = [](const std::string &name, MaterialParameter3f &param)
{
glm::vec4 val = glm::vec4(param.value, 1);
ImGui::ColorButton(*reinterpret_cast<ImVec4*>(&val));
param.value = val.xyz;
ImGui::SameLine();
return ImGui::DragFloat3(name.c_str(), reinterpret_cast<float*>(&param.value), 0.01f, 0.f, 1.f);
};
const static auto floatOption = [](const std::string &name, MaterialParameter1f &param)
{
return ImGui::DragFloat(name.c_str(), &param.value, 0.001f, 0.f, 1.f);
};
bool changed = false;
changed |= colorOption("Base Color", base);
ImGui::Spacing();
changed |= floatOption("Roughness", roughness);
changed |= floatOption("Metallic", metallic);
changed |= floatOption("Transmissive", transmissive);
changed |= ImGui::DragFloat("Emissive", &emission.value, 0.001f, 0.f, 1000.f);
ImGui::Spacing();
changed |= ImGui::DragFloat("IOR", &index_of_refraction, 0.001f, 0.1f, 10.f);
if (changed)
messaging::Handler::getInstance().submit(tags::material, 1);
ImGui::EndPopup();
}
ImGui::PopID();
}
......
......@@ -49,11 +49,10 @@ namespace glare
float index_of_refraction = 1.45f;
std::shared_ptr<TextureRGBA_UB> map_normal;
std::shared_ptr<TextureRGBA_UB> map_displacement;
private:
static std::atomic<size_t> m_current_id;
const size_t m_material_id;
std::string m_name;
};
}
......
......@@ -292,7 +292,7 @@ namespace ImGui {
style->Colors[ImGuiCol_Text] = ImVec4(0.80f, 0.80f, 0.83f, 1.00f);
style->Colors[ImGuiCol_TextDisabled] = ImVec4(0.24f, 0.23f, 0.29f, 1.00f);
style->Colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.05f, 0.07f, 1.00f);
style->Colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.05f, 0.07f, 0.60f);
style->Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.07f, 0.07f, 0.09f, 1.00f);
style->Colors[ImGuiCol_PopupBg] = ImVec4(0.07f, 0.07f, 0.09f, 1.00f);
style->Colors[ImGuiCol_Border] = ImVec4(0.80f, 0.80f, 0.83f, 0.88f);
......
......@@ -7,7 +7,10 @@
#ifndef __IMGUI_GLFW3_IMPL_H
#define __IMGUI_GLFW3_IMPL_H
#include <array>
#include "imgui.h"
#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui_internal.h"
#include "imgui_orient.h"
#include <string>
#include <vector>
......@@ -42,6 +45,159 @@ namespace ImGui {
bool FeatureButton(const char* label, const ImVec2& size = ImVec2(0, 0));
template<int Count>
inline bool VTabs(const std::array<std::string, Count>& names, int& current)
{
auto space = ImGui::GetStyle().ItemSpacing;
ImGui::GetStyle().ItemSpacing = ImVec2(0, 0);
ImGuiWindow* w = GetCurrentWindow();
const ImU32 col = GetColorU32(ImGuiCol_Column);
ImGui::BeginGroup();
bool active = false;
for (auto i = 0; i < Count; ++i)
{
bool active = i == current;
if (VTab(names[i].c_str(), &active))
{
current = i;
active = true;
}
}
ImGui::EndGroup();
float y2 = w->DC.CursorPos.y;
ImGui::SameLine();
float y1 = w->DC.CursorPosPrevLine.y;
float x = w->DC.CursorPos.x-1.f;
w->DrawList->AddLine(ImVec2(x, y1-1.f), ImVec2(x, y2-1.f), col, 2);
ImGui::GetStyle().Colors[ImGuiCol_FrameBg].w *= 0.5f;
ImGui::BeginChildFrame(ImHash(names[0].c_str(), 1), ImVec2(ImGui::GetContentRegionAvailWidth(), 0));
ImGui::GetStyle().Colors[ImGuiCol_FrameBg].w *= 2.f;
ImGui::GetStyle().ItemSpacing = space;
ImGui::SameLine();
return active;
}
template<int Count>
inline bool Tabs(const std::array<std::string, Count>& names, int& current, bool full_width = true)
{
float window_padding = ImGui::GetStyle().WindowPadding.x;
if (full_width)
ImGui::GetStyle().WindowPadding.x = 0;
auto space = ImGui::GetStyle().ItemSpacing;
ImGui::GetStyle().ItemSpacing = ImVec2(0, 0);
ImGuiWindow* w = GetCurrentWindow();
const ImU32 col = GetColorU32(ImGuiCol_Column);
ImGui::BeginGroup();
bool active = false;
for (auto i = 0; i < Count; ++i)
{
bool a = i == current;
auto width = ImGui::GetContentRegionAvailWidth() / static_cast<float>(Count - i);
if (Tab(names[i].c_str(), &a, width))
{
current = i;
active = true;
}
ImGui::SameLine();
}
float x2 = w->DC.CursorPos.x;
ImGui::EndGroup();
float x1 = w->DC.CursorPos.x;
float y = w->DC.CursorPos.y;
w->DrawList->AddLine(ImVec2(x1, y), ImVec2(x2, y), col, 2);
ImGui::GetStyle().WindowPadding.x = window_padding;
ImGui::GetStyle().Colors[ImGuiCol_FrameBg].w *= 0.5f;
ImGui::BeginChildFrame(ImHash(names[0].c_str(), 1), ImVec2(ImGui::GetContentRegionAvailWidth(), 0));
ImGui::GetStyle().Colors[ImGuiCol_FrameBg].w *= 2.f;
ImGui::GetStyle().ItemSpacing = space;
ImGui::NewLine();
return active;
}
inline void EndTabs()
{
ImGui::EndChildFrame();
}