cleanup and refactoring

This commit is contained in:
CDaut 2024-05-25 11:53:25 +02:00
parent 2302158928
commit 76f6bf62a4
Signed by: clara
GPG key ID: 223391B52FAD4463
1285 changed files with 757994 additions and 8 deletions

View file

@ -0,0 +1,43 @@
## Table of Contents
- [imgui_axis.hpp](#imgui_axishpp)
- [imgui_camera_widget.h](#imgui_camera_widgeth)
- [imgui_orient.h](#imgui_orienth)
## imgui_axis.hpp
Function `Axis(ImVec2 pos, const glm::mat4& modelView, float size = 20.f)`
which display right-handed axis in a ImGui window.
Example
```cpp
{ // Display orientation axis at the bottom left corner of the window
float axisSize = 25.F;
ImVec2 pos = ImGui::GetWindowPos();
pos.y += ImGui::GetWindowSize().y;
pos += ImVec2(axisSize * 1.1F, -axisSize * 1.1F) * ImGui::GetWindowDpiScale(); // Offset
ImGuiH::Axis(pos, CameraManip.getMatrix(), axisSize);
}
```
## imgui_camera_widget.h
### functions in ImGuiH
- CameraWidget : CameraWidget is a Camera widget for the the Camera Manipulator
- SetCameraJsonFile : set the name (without .json) of the setting file. It will load and replace all camera and settings
- SetHomeCamera : set the home camera - replace the one on load
- AddCamera : adding a camera to the list of cameras
## imgui_orient.h
### struct ImOrient
> brief This is a really nice implementation of an orientation widget; all due respect to the original author ;)
This is a port of the AntTweakBar orientation widget, which is a 3D orientation widget that allows the user to specify a
3D orientation using a quaternion, axis-angle, or direction vector. It is a very useful widget for 3D applications.

View file

@ -0,0 +1,11 @@
## Table of Contents
- [imgui_impl_gl.h](#imgui_impl_glh)
- [imgui_vk_extra.h](#imgui_vk_extrah)
## imgui_impl_gl.h
> Todo: Add documentation
## imgui_vk_extra.h
> Todo: Add documentation

View file

@ -0,0 +1,289 @@
#include "imgui/backends/imgui_impl_gl.h"
#include "imgui.h"
#include <include_gl.h>
// ImGui GLFW binding with OpenGL3 + shaders
// More or less copy-pasted from https://raw.githubusercontent.com/ocornut/imgui/master/examples/opengl3_example/imgui_impl_glfw_gl3.cpp
namespace {
const char* g_GlslVersion = "#version 450\n";
GLuint g_FontTexture = 0;
int g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0;
int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0;
unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
} // namespace
void ImGui::InitGL()
{
// Backup GL state
GLint last_texture, last_array_buffer, last_vertex_array;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
const GLchar* vertex_shader =
"layout(location=0) uniform mat4 ProjMtx;\n"
"layout(location=0) in vec2 Position;\n"
"layout(location=1) in vec2 UV;\n"
"layout(location=2) in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* fragment_shader =
"layout(location=1, binding=0) uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"layout(location=0, index=0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n"
"}\n";
const GLchar* vertex_shader_with_version[2] = {g_GlslVersion, vertex_shader};
const GLchar* fragment_shader_with_version[2] = {g_GlslVersion, fragment_shader};
g_ShaderHandle = glCreateProgram();
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
glCompileShader(g_VertHandle);
glCompileShader(g_FragHandle);
glAttachShader(g_ShaderHandle, g_VertHandle);
glAttachShader(g_ShaderHandle, g_FragHandle);
glLinkProgram(g_ShaderHandle);
// check if program linked
GLint success = 0;
glGetProgramiv(g_ShaderHandle, GL_LINK_STATUS, &success);
if(!success)
{
char temp[1024];
glGetProgramInfoLog(g_ShaderHandle, 1024, 0, temp);
success = success;
}
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position");
g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV");
g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color");
glGenBuffers(1, &g_VboHandle);
glGenBuffers(1, &g_ElementsHandle);
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
// Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// Upload texture to graphics system
glGenTextures(1, &g_FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts->TexID = (void*)(intptr_t)g_FontTexture;
// Restore state
glBindTexture(GL_TEXTURE_2D, last_texture);
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBindVertexArray(last_vertex_array);
}
void ImGui::ShutdownGL()
{
if(g_VboHandle)
glDeleteBuffers(1, &g_VboHandle);
if(g_ElementsHandle)
glDeleteBuffers(1, &g_ElementsHandle);
g_VboHandle = g_ElementsHandle = 0;
if(g_ShaderHandle && g_VertHandle)
glDetachShader(g_ShaderHandle, g_VertHandle);
if(g_VertHandle)
glDeleteShader(g_VertHandle);
g_VertHandle = 0;
if(g_ShaderHandle && g_FragHandle)
glDetachShader(g_ShaderHandle, g_FragHandle);
if(g_FragHandle)
glDeleteShader(g_FragHandle);
g_FragHandle = 0;
if(g_ShaderHandle)
glDeleteProgram(g_ShaderHandle);
g_ShaderHandle = 0;
if(g_FontTexture)
{
glDeleteTextures(1, &g_FontTexture);
ImGui::GetIO().Fonts->TexID = 0;
g_FontTexture = 0;
}
}
void ImGui::RenderDrawDataGL(const ImDrawData* drawData)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
ImGuiIO& io = ImGui::GetIO();
int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x);
int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y);
if(fb_width == 0 || fb_height == 0)
return;
// evil
((ImDrawData*)drawData)->ScaleClipRects(io.DisplayFramebufferScale);
// Backup GL state
GLenum last_active_texture;
glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLint last_program;
glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
GLint last_sampler;
glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler);
GLint last_array_buffer;
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
GLint last_element_array_buffer;
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer);
GLint last_vertex_array;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
GLint last_polygon_mode[2];
glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
GLint last_viewport[4];
glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4];
glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
GLenum last_blend_src_rgb;
glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
GLenum last_blend_dst_rgb;
glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
GLenum last_blend_src_alpha;
glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
GLenum last_blend_dst_alpha;
glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
GLenum last_blend_equation_rgb;
glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
GLenum last_blend_equation_alpha;
glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// Setup viewport, orthographic projection matrix
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
const float ortho_projection[4][4] = {
{2.0f / io.DisplaySize.x, 0.0f, 0.0f, 0.0f},
{0.0f, 2.0f / -io.DisplaySize.y, 0.0f, 0.0f},
{0.0f, 0.0f, -1.0f, 0.0f},
{-1.0f, 1.0f, 0.0f, 1.0f},
};
glUseProgram(g_ShaderHandle);
glUniform1i(g_AttribLocationTex, 0);
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
// Recreate the VAO every time
// (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.)
GLuint vao_handle = 0;
glGenVertexArrays(1, &vao_handle);
glBindVertexArray(vao_handle);
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glEnableVertexAttribArray(g_AttribLocationPosition);
glEnableVertexAttribArray(g_AttribLocationUV);
glEnableVertexAttribArray(g_AttribLocationColor);
glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert),
(GLvoid*)IM_OFFSETOF(ImDrawVert, col));
// Draw
for(int n = 0; n < drawData->CmdListsCount; n++)
{
const ImDrawList* cmd_list = drawData->CmdLists[n];
const ImDrawIdx* idx_buffer_offset = 0;
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert),
(const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx),
(const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
for(int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if(pcmd->UserCallback)
{
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w),
(int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y));
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount,
sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
}
idx_buffer_offset += pcmd->ElemCount;
}
}
glDeleteVertexArrays(1, &vao_handle);
// Restore modified GL state
glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindSampler(0, last_sampler);
glActiveTexture(last_active_texture);
glBindVertexArray(last_vertex_array);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer);
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
if(last_enable_blend)
glEnable(GL_BLEND);
else
glDisable(GL_BLEND);
if(last_enable_cull_face)
glEnable(GL_CULL_FACE);
else
glDisable(GL_CULL_FACE);
if(last_enable_depth_test)
glEnable(GL_DEPTH_TEST);
else
glDisable(GL_DEPTH_TEST);
if(last_enable_scissor_test)
glEnable(GL_SCISSOR_TEST);
else
glDisable(GL_SCISSOR_TEST);
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
}

View file

@ -0,0 +1,12 @@
#pragma once
/// @DOC_SKIP (keyword to exclude this file from automatic README.md generation)
struct ImDrawData;
namespace ImGui {
void InitGL();
void ShutdownGL();
void RenderDrawDataGL(const ImDrawData* drawData);
} // namespace ImGui

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2021, 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) 2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include "imgui/backends/imgui_vk_extra.h"
#include "imgui/imgui_helper.h"
#include <backends/imgui_impl_vulkan.h>
static ImGui_ImplVulkan_InitInfo g_VulkanInitInfo = {};
static void check_vk_result(VkResult err)
{
assert(err == VK_SUCCESS);
}
void ImGui::InitVK(VkDevice device, VkPhysicalDevice physicalDevice, VkQueue queue, uint32_t queueFamilyIndex, VkRenderPass pass, int subPassIndex)
{
VkResult err = VK_RESULT_MAX_ENUM;
std::vector<VkDescriptorPoolSize> poolSize{{VK_DESCRIPTOR_TYPE_SAMPLER, 1}, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1}};
VkDescriptorPoolCreateInfo poolInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
poolInfo.maxSets = 2;
poolInfo.poolSizeCount = static_cast<uint32_t>(poolSize.size());
poolInfo.pPoolSizes = poolSize.data();
vkCreateDescriptorPool(device, &poolInfo, nullptr, &g_VulkanInitInfo.DescriptorPool);
// Setup Platform/Renderer backends
ImGui_ImplVulkan_InitInfo init_info = {};
init_info.Instance = VkInstance(666); // <--- WRONG need argument
init_info.PhysicalDevice = physicalDevice;
init_info.Device = device;
init_info.QueueFamily = queueFamilyIndex;
init_info.Queue = queue;
init_info.PipelineCache = VK_NULL_HANDLE;
init_info.DescriptorPool = g_VulkanInitInfo.DescriptorPool;
init_info.RenderPass = pass;
init_info.Subpass = subPassIndex;
init_info.MinImageCount = 2;
init_info.ImageCount = 3; // <--- WRONG need argument
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; // <--- need argument?
init_info.Allocator = nullptr;
init_info.CheckVkResultFn = nullptr;
ImGui_ImplVulkan_Init(&init_info);
g_VulkanInitInfo = init_info;
// Upload Fonts
VkCommandPool pool = VK_NULL_HANDLE;
VkCommandPoolCreateInfo createInfo{VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};
createInfo.queueFamilyIndex = queueFamilyIndex;
err = vkCreateCommandPool(device, &createInfo, nullptr, &pool);
check_vk_result(err);
VkCommandBuffer cmd = VK_NULL_HANDLE;
VkCommandBufferAllocateInfo allocInfo{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
allocInfo.commandPool = pool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1;
err = vkAllocateCommandBuffers(device, &allocInfo, &cmd);
check_vk_result(err);
VkCommandBufferBeginInfo beginInfo{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
err = vkBeginCommandBuffer(cmd, &beginInfo);
check_vk_result(err);
ImGui_ImplVulkan_CreateFontsTexture();
err = vkEndCommandBuffer(cmd);
check_vk_result(err);
VkSubmitInfo submit{VK_STRUCTURE_TYPE_SUBMIT_INFO};
submit.commandBufferCount = 1;
submit.pCommandBuffers = &cmd;
err = vkQueueSubmit(queue, 1, &submit, VK_NULL_HANDLE);
check_vk_result(err);
err = vkDeviceWaitIdle(device);
check_vk_result(err);
vkFreeCommandBuffers(device, pool, 1, &cmd);
vkDestroyCommandPool(device, pool, nullptr);
}
void ImGui::ShutdownVK()
{
ImGui_ImplVulkan_InitInfo* v = &g_VulkanInitInfo;
ImGui_ImplVulkan_Shutdown();
vkDestroyDescriptorPool(v->Device, v->DescriptorPool, v->Allocator);
}

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2021, 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) 2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
/// @DOC_SKIP (keyword to exclude this file from automatic README.md generation)
#include <backends/imgui_impl_vulkan.h>
namespace ImGui {
void InitVK(VkDevice device, VkPhysicalDevice physicalDevice, VkQueue queue, uint32_t queueFamilyIndex, VkRenderPass pass, int subPassIndex = 0);
void ShutdownVK();
} // namespace ImGui

View file

@ -0,0 +1,189 @@
/*
* Copyright (c) 2022-2023, 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) 2014-2022 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include <vector>
#include <array>
#define _USE_MATH_DEFINES
#include <math.h>
#include "imgui_axis.hpp"
#include <glm/glm.hpp>
//////////////////////////////////////////////////////////////////////////
// This IMGUI widget draw axis in red, green and blue at the position
// defined.
//
struct AxisGeom
{
AxisGeom()
{
const float asize = 1.0f; // length of arrow
const float aradius = 0.11f; // width of arrow tip
const float abase = 0.66f; // 1/3 of arrow length
const int asubdiv = 8;
// Cone
red.push_back({asize, 0, 0}); // 0 Tip
for(int i = 0; i <= asubdiv; ++i)
{
float a0 = 2.0F * float(M_PI) * (float(i)) / asubdiv; // Counter-clockwise
float y0 = cosf(a0) * aradius;
float z0 = sinf(a0) * aradius;
red.push_back({abase, y0, z0});
}
for(int i = 0; i <= asubdiv - 1; ++i) // Triangle fan
{
indices.push_back(0);
indices.push_back(i + 1);
indices.push_back(i + 2);
}
// Under Cap
int center = static_cast<int>(red.size());
red.push_back({abase, 0, 0}); // Center of cap
for(int i = 0; i <= asubdiv; ++i)
{
float a0 = -2.0F * float(M_PI) * (float(i)) / asubdiv; // Clockwise
float y0 = cosf(a0) * aradius;
float z0 = sinf(a0) * aradius;
red.push_back({abase, y0, z0});
}
for(int i = 0; i <= asubdiv - 1; ++i)
{
indices.push_back(center);
indices.push_back(center + i + 1);
indices.push_back(center + i + 2);
}
// Start of arrow
red.push_back({0, 0, 0});
// Other arrows are permutations of the Red arrow
for(const auto& v : red)
{
green.push_back({v.z, v.x, v.y});
blue.push_back({v.y, v.z, v.x});
}
}
std::vector<glm::vec3> red;
std::vector<glm::vec3> green;
std::vector<glm::vec3> blue;
std::vector<int> indices;
// Return the transformed arrow
std::vector<glm::vec3> transform(const std::vector<glm::vec3>& in_vec, const ImVec2& pos, const glm::mat4& modelView, float size)
{
std::vector<glm::vec3> temp(in_vec.size());
for(size_t i = 0; i < in_vec.size(); ++i)
{
temp[i] = glm::vec3(modelView * glm::vec4(in_vec[i], 0.F)); // Rotate
temp[i].x *= size; // Scale
temp[i].y *= -size; // - invert Y
temp[i].x += pos.x; // Translate
temp[i].y += pos.y;
}
return temp;
}
void drawTriangle(ImVec2 v0, ImVec2 v1, ImVec2 v2, const ImVec2& uv, ImU32 col)
{
auto draw_list = ImGui::GetWindowDrawList();
ImVec2 d0 = ImVec2(v1.x - v0.x, v1.y - v0.y);
ImVec2 d1 = ImVec2(v2.x - v0.x, v2.y - v0.y);
float c = (d0.x * d1.y) - (d0.y * d1.x); // Cross
if(c > 0.0f) // Culling to avoid z-fighting
{
v1 = v0; // Culled triangles are degenerated to
v2 = v0; // avoid displaying them
}
draw_list->PrimVtx(v0, uv, col);
draw_list->PrimVtx(v1, uv, col);
draw_list->PrimVtx(v2, uv, col);
}
// Draw the arrow
void draw(const std::vector<glm::vec3>& vertex, ImU32 col)
{
auto draw_list = ImGui::GetWindowDrawList();
const ImVec2 uv = ImGui::GetFontTexUvWhitePixel();
int num_indices = static_cast<int>(indices.size());
draw_list->PrimReserve(num_indices, num_indices); // num vert/indices
// Draw all triangles
for(int i = 0; i < num_indices; i += 3)
{
int i0 = indices[i];
int i1 = indices[i + 1];
int i2 = indices[i + 2];
ImVec2 v0 = {vertex[i0].x, vertex[i0].y};
ImVec2 v1 = {vertex[i1].x, vertex[i1].y};
ImVec2 v2 = {vertex[i2].x, vertex[i2].y};
drawTriangle(v0, v1, v2, uv, col);
}
// Draw the line
draw_list->AddLine(ImVec2(vertex[0].x, vertex[0].y), ImVec2(vertex.back().x, vertex.back().y), col,
1.0F * ImGui::GetWindowDpiScale());
}
};
void ImGuiH::Axis(ImVec2 pos, const glm::mat4& modelView, float size /*= 20.f*/)
{
static AxisGeom a;
struct Arrow
{
std::vector<glm::vec3> v;
ImU32 c{0};
};
size *= ImGui::GetWindowDpiScale();
std::array<Arrow, 3> arrow;
arrow[0].v = a.transform(a.red, pos, modelView, size);
arrow[0].c = IM_COL32(200, 0, 0, 255);
arrow[1].v = a.transform(a.green, pos, modelView, size);
arrow[1].c = IM_COL32(0, 200, 0, 255);
arrow[2].v = a.transform(a.blue, pos, modelView, size);
arrow[2].c = IM_COL32(0, 0, 200, 255);
// Sort from smallest Z to nearest (Painter algorithm)
if(arrow[1].v[0].z < arrow[0].v[0].z)
std::swap(arrow[0], arrow[1]);
if(arrow[2].v[0].z < arrow[1].v[0].z)
{
std::swap(arrow[1], arrow[2]);
if(arrow[1].v[0].z < arrow[0].v[0].z)
std::swap(arrow[1], arrow[0]);
}
// Draw all axis
a.draw(arrow[0].v, arrow[0].c);
a.draw(arrow[1].v, arrow[1].c);
a.draw(arrow[2].v, arrow[2].c);
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2022-2023, 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) 2014-2022 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include "imgui.h"
#include <glm/glm.hpp>
/* @DOC_START -------------------------------------------------------
Function `Axis(ImVec2 pos, const glm::mat4& modelView, float size = 20.f)`
which display right-handed axis in a ImGui window.
Example
```cpp
{ // Display orientation axis at the bottom left corner of the window
float axisSize = 25.F;
ImVec2 pos = ImGui::GetWindowPos();
pos.y += ImGui::GetWindowSize().y;
pos += ImVec2(axisSize * 1.1F, -axisSize * 1.1F) * ImGui::GetWindowDpiScale(); // Offset
ImGuiH::Axis(pos, CameraManip.getMatrix(), axisSize);
}
```
--- @DOC_END ------------------------------------------------------- */
// The API
namespace ImGuiH {
// This utility is adding the 3D axis at `pos`, using the matrix `modelView`
IMGUI_API void Axis(ImVec2 pos, const glm::mat4& modelView, float size = 20.f);
}; // namespace ImGuiH

View file

@ -0,0 +1,511 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* NVIDIA CORPORATION and its licensors retain all intellectual property
* and proprietary rights in and to this software, related documentation
* and any modifications thereto. Any use, reproduction, disclosure or
* distribution of this software and related documentation without an express
* license agreement from NVIDIA CORPORATION is strictly prohibited.
*/
#include "json.hpp"
#include "imgui.h"
#include "imgui/imgui_camera_widget.h"
#include "imgui_helper.h"
#include "nvh/cameramanipulator.hpp"
#include "nvh/misc.hpp"
#include <fstream>
#include <sstream>
namespace ImGuiH {
using nlohmann::json;
using Gui = ImGuiH::Control;
//--------------------------------------------------------------------------------------------------
// Holds all saved cameras in a vector of Cameras
// - The first camera in the list is the HOME camera, the one that was set before this is called.
// - The update function will check if something has changed and will save the JSON to disk, only
// once in a while.
// - Adding a camera will be added only if it is different from all other saved cameras
// - load/save Setting will load next to the executable, the "jsonFilename" + ".json"
//
struct CameraManager
{
CameraManager() = default;
~CameraManager()
{
if(m_settingsDirtyTimer > 0.0f)
saveSetting(nvh::CameraManipulator::Singleton());
};
// update setting, load or save
void update(nvh::CameraManipulator& cameraM)
{
// Push the HOME camera and load default setting
if(m_cameras.empty())
{
m_cameras.emplace_back(cameraM.getCamera());
}
if(m_doLoadSetting)
loadSetting(cameraM);
// Save settings (with a delay after the last modification, so we don't spam disk too much)
auto& IO = ImGui::GetIO();
if(m_settingsDirtyTimer > 0.0f)
{
m_settingsDirtyTimer -= IO.DeltaTime;
if(m_settingsDirtyTimer <= 0.0f)
{
saveSetting(cameraM);
m_settingsDirtyTimer = 0.0f;
}
}
}
// Clear all cameras except the HOME
void removedSavedCameras()
{
if(m_cameras.size() > 1)
m_cameras.erase(m_cameras.begin() + 1, m_cameras.end());
}
void setCameraJsonFile(const std::string& filename)
{
m_jsonFilename = NVPSystem::exePath() + filename + ".json";
m_doLoadSetting = true;
removedSavedCameras();
}
void setHomeCamera(const nvh::CameraManipulator::Camera& camera)
{
if(m_cameras.empty())
m_cameras.resize(1);
m_cameras[0] = camera;
}
// Adding a camera only if it different from all the saved ones
void addCamera(const nvh::CameraManipulator::Camera& camera)
{
bool unique = true;
for(const auto& c : m_cameras)
{
if(c == camera)
{
unique = false;
break;
}
}
if(unique)
{
m_cameras.emplace_back(camera);
markIniSettingsDirty();
}
}
// Removing a camera
void removeCamera(int delete_item)
{
m_cameras.erase(m_cameras.begin() + delete_item);
markIniSettingsDirty();
}
void markIniSettingsDirty()
{
if(m_settingsDirtyTimer <= 0.0f)
m_settingsDirtyTimer = 0.1f;
}
template <typename T>
bool getJsonValue(const json& j, const std::string& name, T& value)
{
auto fieldIt = j.find(name);
if(fieldIt != j.end())
{
value = (*fieldIt);
return true;
}
LOGE("Could not find JSON field %s", name.c_str());
return false;
}
template <typename T>
bool getJsonArray(const json& j, const std::string& name, T& value)
{
auto fieldIt = j.find(name);
if(fieldIt != j.end())
{
value = T((*fieldIt).begin(), (*fieldIt).end());
return true;
}
LOGE("Could not find JSON field %s", name.c_str());
return false;
}
void loadSetting(nvh::CameraManipulator& cameraM)
{
if(m_jsonFilename.empty() || m_cameras.empty() || m_doLoadSetting == false)
return;
try
{
m_doLoadSetting = false;
// Clear all cameras except the HOME
removedSavedCameras();
std::ifstream i(m_jsonFilename);
if(!i.is_open())
return;
// Parsing the file
json j;
i >> j;
// Temp
int iVal;
float fVal;
std::vector<float> vfVal;
// Settings
if(getJsonValue(j, "mode", iVal))
cameraM.setMode(static_cast<nvh::CameraManipulator::Modes>(iVal));
if(getJsonValue(j, "speed", fVal))
cameraM.setSpeed(fVal);
if(getJsonValue(j, "anim_duration", fVal))
cameraM.setAnimationDuration(fVal);
// All cameras
std::vector<json> cc;
getJsonArray(j, "cameras", cc);
for(auto& c : cc)
{
nvh::CameraManipulator::Camera camera;
if(getJsonArray(c, "eye", vfVal))
camera.eye = {vfVal[0], vfVal[1], vfVal[2]};
if(getJsonArray(c, "ctr", vfVal))
camera.ctr = {vfVal[0], vfVal[1], vfVal[2]};
if(getJsonArray(c, "up", vfVal))
camera.up = {vfVal[0], vfVal[1], vfVal[2]};
if(getJsonValue(c, "fov", fVal))
camera.fov = fVal;
m_cameras.emplace_back(camera);
}
i.close();
}
catch(...)
{
return;
}
}
void saveSetting(nvh::CameraManipulator& cameraM) noexcept
{
if(m_jsonFilename.empty())
return;
try
{
json j;
j["mode"] = cameraM.getMode();
j["speed"] = cameraM.getSpeed();
j["anim_duration"] = cameraM.getAnimationDuration();
// Save all extra cameras
json cc = json::array();
for(size_t n = 1; n < m_cameras.size(); n++)
{
auto& c = m_cameras[n];
json jo = json::object();
jo["eye"] = std::vector<float>{c.eye.x, c.eye.y, c.eye.z};
jo["up"] = std::vector<float>{c.up.x, c.up.y, c.up.z};
jo["ctr"] = std::vector<float>{c.ctr.x, c.ctr.y, c.ctr.z};
jo["fov"] = c.fov;
cc.push_back(jo);
}
j["cameras"] = cc;
std::ofstream o(m_jsonFilename);
if(o.is_open())
{
o << j.dump(2) << std::endl;
o.close();
}
}
catch(const std::exception& e)
{
LOGE("Could not save camera settings to %s: %s\n", m_jsonFilename.c_str(), e.what());
}
}
// Holds all cameras. [0] == HOME
std::vector<nvh::CameraManipulator::Camera> m_cameras;
float m_settingsDirtyTimer{0};
std::string m_jsonFilename;
bool m_doLoadSetting{true};
};
static std::unique_ptr<CameraManager> sCamMgr;
//--------------------------------------------------------------------------------------------------
// Display the values of the current camera: position, center, up and FOV
//
void CurrentCameraTab(nvh::CameraManipulator& cameraM, nvh::CameraManipulator::Camera& camera, bool& changed, bool& instantSet)
{
bool y_is_up = camera.up.y == 1;
PropertyEditor::begin();
PropertyEditor::entry(
"Eye", [&] { return ImGui::InputFloat3("##Eye", &camera.eye.x, "%.5f"); }, "Position of the Camera");
changed |= ImGui::IsItemDeactivatedAfterEdit();
PropertyEditor::entry(
"Center", [&] { return ImGui::InputFloat3("##Ctr", &camera.ctr.x, "%.5f"); }, "Center of camera interest");
changed |= ImGui::IsItemDeactivatedAfterEdit();
changed |= PropertyEditor::entry(
"Y is UP", [&] { return ImGui::Checkbox("##Y", &y_is_up); }, "Is Y pointing up or Z?");
if(PropertyEditor::entry(
"FOV",
[&] { return ImGui::SliderFloat("##Y", &camera.fov, 1.F, 179.F, "%.1f deg", ImGuiSliderFlags_Logarithmic); },
"Field of view in degrees"))
{
instantSet = true;
changed = true;
}
if(PropertyEditor::treeNode("Clip planes"))
{
glm::vec2 clip = cameraM.getClipPlanes();
PropertyEditor::entry("Near", [&] { return ImGui::InputFloat("##CN", &clip.x); });
changed |= ImGui::IsItemDeactivatedAfterEdit();
PropertyEditor::entry("Far", [&] { return ImGui::InputFloat("##CF", &clip.y); });
changed |= ImGui::IsItemDeactivatedAfterEdit();
PropertyEditor::treePop();
cameraM.setClipPlanes(clip);
}
camera.up = y_is_up ? glm::vec3(0, 1, 0) : glm::vec3(0, 0, 1);
if(cameraM.isAnimated())
{
// Ignoring any changes while the camera is moving to the goal.
// The camera has to be in the new position before setting a new value.
changed = false;
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextDisabled("(?)");
ImGuiH::tooltip(cameraM.getHelp().c_str(), false, 0.0f);
ImGui::TableNextColumn();
if(ImGui::SmallButton("Copy"))
{
std::string text = nvh::stringFormat("{%.5f, %.5f, %.5f}, {%.5f, %.5f, %.5f}, {%.5f, %.5f, %.5f}", //
camera.eye.x, camera.eye.y, camera.eye.z, //
camera.ctr.x, camera.ctr.y, camera.ctr.z, //
camera.up.x, camera.up.y, camera.up.z);
ImGui::SetClipboardText(text.c_str());
}
ImGuiH::tooltip("Copy to the clipboard the current camera: {eye}, {ctr}, {up}");
ImGui::SameLine();
const char* pPastedString;
if(ImGui::SmallButton("Paste") && (pPastedString = ImGui::GetClipboardText()))
{
float val[9]{};
int result = sscanf(pPastedString, "{%f, %f, %f}, {%f, %f, %f}, {%f, %f, %f}", &val[0], &val[1], &val[2], &val[3],
&val[4], &val[5], &val[6], &val[7], &val[8]);
if(result == 9) // 9 value properly scanned
{
camera.eye = glm::vec3{val[0], val[1], val[2]};
camera.ctr = glm::vec3{val[3], val[4], val[5]};
camera.up = glm::vec3{val[6], val[7], val[8]};
changed = true;
}
}
ImGuiH::tooltip("Paste from the clipboard the current camera: {eye}, {ctr}, {up}");
PropertyEditor::end();
}
//--------------------------------------------------------------------------------------------------
// Display buttons for all saved cameras. Allow to create and delete saved cameras
//
void SavedCameraTab(nvh::CameraManipulator& cameraM, nvh::CameraManipulator::Camera& camera, bool& changed)
{
// Dummy
ImVec2 button_sz(50, 30);
char label[128];
ImGuiStyle& style = ImGui::GetStyle();
int buttons_count = (int)sCamMgr->m_cameras.size();
float window_visible_x2 = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x;
// The HOME camera button, different from the other ones
if(ImGui::Button("Home", ImVec2(ImGui::GetWindowContentRegionMax().x, 50)))
{
camera = sCamMgr->m_cameras[0];
changed = true;
}
ImGuiH::tooltip("Reset the camera to its origin");
// Display all the saved camera in an array of buttons
int delete_item = -1;
for(int n = 1; n < buttons_count; n++)
{
ImGui::PushID(n);
sprintf(label, "# %d", n);
if(ImGui::Button(label, button_sz))
{
camera = sCamMgr->m_cameras[n];
changed = true;
}
// Middle click to delete a camera
if(ImGui::IsItemHovered() && ImGui::GetIO().MouseClicked[NVPWindow::MOUSE_BUTTON_MIDDLE])
delete_item = n;
// Displaying the position of the camera when hovering the button
sprintf(label, "Pos: %3.5f, %3.5f, %3.5f", sCamMgr->m_cameras[n].eye.x, sCamMgr->m_cameras[n].eye.y,
sCamMgr->m_cameras[n].eye.z);
ImGuiH::tooltip(label);
// Wrapping all buttons (see ImGUI Demo)
float last_button_x2 = ImGui::GetItemRectMax().x;
float next_button_x2 = last_button_x2 + style.ItemSpacing.x + button_sz.x; // Expected position if next button was on same line
if(n + 1 < buttons_count && next_button_x2 < window_visible_x2)
ImGui::SameLine();
ImGui::PopID();
}
// Adding a camera button
if(ImGui::Button("+"))
{
sCamMgr->addCamera(cameraM.getCamera());
}
ImGuiH::tooltip("Add a new saved camera");
ImGui::SameLine();
ImGui::TextDisabled("(?)");
ImGuiH::tooltip("Middle-click a camera to delete it", false, 0.0f);
// Remove element
if(delete_item > 0)
{
sCamMgr->removeCamera(delete_item);
}
}
//--------------------------------------------------------------------------------------------------
// This holds all camera setting, like the speed, the movement mode, transition duration
//
void CameraExtraTab(nvh::CameraManipulator& cameraM, bool& changed)
{
// Navigation Mode
PropertyEditor::begin();
auto mode = cameraM.getMode();
auto speed = cameraM.getSpeed();
auto duration = static_cast<float>(cameraM.getAnimationDuration());
changed |= PropertyEditor::entry(
"Navigation",
[&] {
int rmode = static_cast<int>(mode);
changed |= ImGui::RadioButton("Examine", &rmode, nvh::CameraManipulator::Examine);
ImGuiH::tooltip("The camera orbit around a point of interest");
changed |= ImGui::RadioButton("Fly", &rmode, nvh::CameraManipulator::Fly);
ImGuiH::tooltip("The camera is free and move toward the looking direction");
changed |= ImGui::RadioButton("Walk", &rmode, nvh::CameraManipulator::Walk);
ImGuiH::tooltip("The camera is free but stay on a plane");
cameraM.setMode(static_cast<nvh::CameraManipulator::Modes>(rmode));
return changed;
},
"Camera Navigation Mode");
changed |= PropertyEditor::entry(
"Speed", [&] { return ImGui::SliderFloat("##S", &speed, 0.01F, 10.0F); }, "Changing the default speed movement");
changed |= PropertyEditor::entry(
"Transition", [&] { return ImGui::SliderFloat("##S", &duration, 0.0F, 2.0F); }, "Nb seconds to move to new position");
cameraM.setSpeed(speed);
cameraM.setAnimationDuration(duration);
PropertyEditor::end();
}
//--------------------------------------------------------------------------------------------------
// Display the camera eye and center of interest position of the camera (nvh::CameraManipulator)
// Allow also to modify the field-of-view (FOV)
// And basic control information is displayed
bool CameraWidget(nvh::CameraManipulator& cameraM /*= nvh::CameraManipulator::Singleton()*/)
{
if(!sCamMgr)
sCamMgr = std::make_unique<CameraManager>();
bool changed{false};
bool instantSet{false};
auto camera = cameraM.getCamera();
// Updating the camera manager
sCamMgr->update(cameraM);
// Starting UI
if(ImGui::BeginTabBar("Hello"))
{
if(ImGui::BeginTabItem("Current"))
{
CurrentCameraTab(cameraM, camera, changed, instantSet);
ImGui::EndTabItem();
}
if(ImGui::BeginTabItem("Cameras"))
{
SavedCameraTab(cameraM, camera, changed);
ImGui::EndTabItem();
}
if(ImGui::BeginTabItem("Extra"))
{
CameraExtraTab(cameraM, changed);
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
// Apply the change back to the camera
if(changed)
{
cameraM.setCamera(camera, instantSet);
}
ImGui::Separator();
return changed;
}
void SetCameraJsonFile(const std::string& filename)
{
if(!sCamMgr)
sCamMgr = std::make_unique<CameraManager>();
sCamMgr->setCameraJsonFile(filename);
}
void SetHomeCamera(const nvh::CameraManipulator::Camera& camera)
{
if(!sCamMgr)
sCamMgr = std::make_unique<CameraManager>();
sCamMgr->setHomeCamera(camera);
}
void AddCamera(const nvh::CameraManipulator::Camera& camera)
{
if(!sCamMgr)
sCamMgr = std::make_unique<CameraManager>();
sCamMgr->addCamera(camera);
}
} // namespace ImGuiH

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* NVIDIA CORPORATION and its licensors retain all intellectual property
* and proprietary rights in and to this software, related documentation
* and any modifications thereto. Any use, reproduction, disclosure or
* distribution of this software and related documentation without an express
* license agreement from NVIDIA CORPORATION is strictly prohibited.
*/
#pragma once
#include "nvh/cameramanipulator.hpp"
#include <glm/glm.hpp>
namespace ImGuiH {
/* @DOC_START -------------------------------------------------------
# functions in ImGuiH
- CameraWidget : CameraWidget is a Camera widget for the the Camera Manipulator
- SetCameraJsonFile : set the name (without .json) of the setting file. It will load and replace all camera and settings
- SetHomeCamera : set the home camera - replace the one on load
- AddCamera : adding a camera to the list of cameras
--- @DOC_END ------------------------------------------------------- */
bool CameraWidget(nvh::CameraManipulator& cameraM = nvh::CameraManipulator::Singleton());
void SetCameraJsonFile(const std::string& filename);
void SetHomeCamera(const nvh::CameraManipulator::Camera& camera);
void AddCamera(const nvh::CameraManipulator::Camera& camera);
} // namespace ImGuiH

View file

@ -0,0 +1,690 @@
/*
* 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
*/
#define GLFW_INCLUDE_NONE
#include "imgui/imgui_helper.h"
#include <backends/imgui_impl_glfw.h>
#include <GLFW/glfw3.h>
#include <math.h>
#include <fstream>
namespace ImGuiH {
void Init(int width, int height, void* userData, FontMode fontmode)
{
ImGui::CreateContext();
setFonts(fontmode);
auto& imgui_io = ImGui::GetIO();
imgui_io.IniFilename = nullptr;
imgui_io.UserData = userData;
imgui_io.DisplaySize = ImVec2(float(width), float(height));
imgui_io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable keyboard controls (tab, space, arrow keys)
// Scale style sizes for high-DPI monitors
ImGuiStyle& imgui_style = ImGui::GetStyle();
imgui_style.ScaleAllSizes(getDPIScale());
}
void Deinit()
{
ImGui::DestroyContext(nullptr);
}
bool Combo(const char* label, size_t numEnums, const Enum* enums, void* valuePtr, ImGuiComboFlags flags, ValueType valueType, bool* valueChanged)
{
int* ivalue = (int*)valuePtr;
float* fvalue = (float*)valuePtr;
size_t idx = 0;
bool found = false;
bool changed = false;
for(size_t i = 0; i < numEnums; i++)
{
switch(valueType)
{
case TYPE_INT:
if(enums[i].ivalue == *ivalue)
{
idx = i;
found = true;
}
break;
case TYPE_FLOAT:
if(enums[i].fvalue == *fvalue)
{
idx = i;
found = true;
}
break;
default:
break;
}
}
if(!found)
{
assert(!"No such value in combo!");
return false;
}
if(ImGui::BeginCombo(label, enums[idx].name.c_str(), flags)) // The second parameter is the label previewed before opening the combo.
{
for(size_t i = 0; i < numEnums; i++)
{
ImGui::BeginDisabled(enums[i].disabled);
bool is_selected = i == idx;
if(ImGui::Selectable(enums[i].name.c_str(), is_selected))
{
switch(valueType)
{
case TYPE_INT:
*ivalue = enums[i].ivalue;
break;
case TYPE_FLOAT:
*fvalue = enums[i].fvalue;
break;
default:
break;
}
changed = true;
}
if(is_selected)
{
ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch)
}
ImGui::EndDisabled();
}
ImGui::EndCombo();
}
if(valueChanged)
*valueChanged = changed;
return changed;
}
//--------------------------------------------------------------------------------------------------
//
// If GLFW has been initialized, returns the DPI scale of the primary monitor. Otherwise, returns 1.
//
float getDPIScale()
{
// Cached DPI scale, so that this doesn't change after the first time code calls getDPIScale.
// A negative value indicates that the value hasn't been computed yet.
static float cached_dpi_scale = -1.0f;
if(cached_dpi_scale < 0.0f)
{
// Compute the product of the monitor DPI scale and any DPI scale
// set in the NVPRO_DPI_SCALE variable.
cached_dpi_scale = 1.0f;
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
assert(monitor);
if(monitor != nullptr)
{
float y_scale;
glfwGetMonitorContentScale(monitor, &cached_dpi_scale, &y_scale);
}
// Otherwise, GLFW isn't initialized yet, but might be in the future.
// (Note that this code assumes all samples use GLFW.)
// Multiply by the value of the NVPRO_DPI_SCALE environment variable.
const char* dpi_env = getenv("NVPRO_DPI_SCALE");
if(dpi_env)
{
const float parsed_dpi_env = strtof(dpi_env, nullptr);
if(parsed_dpi_env != 0.0f)
{
cached_dpi_scale *= parsed_dpi_env;
}
}
cached_dpi_scale = (cached_dpi_scale > 0.0f ? cached_dpi_scale : 1.0f);
}
return cached_dpi_scale;
}
//--------------------------------------------------------------------------------------------------
// Setting a dark style for the GUI
// The colors were coded in sRGB color space, set the useLinearColor
// flag to convert to linear color space.
void setStyle(bool useLinearColor)
{
typedef ImVec4 (*srgbFunction)(float, float, float, float);
srgbFunction passthrough = [](float r, float g, float b, float a) -> ImVec4 { return ImVec4(r, g, b, a); };
srgbFunction toLinear = [](float r, float g, float b, float a) -> ImVec4 {
auto toLinearScalar = [](float u) -> float {
return u <= 0.04045 ? 25 * u / 323.f : powf((200 * u + 11) / 211.f, 2.4f);
};
return ImVec4(toLinearScalar(r), toLinearScalar(g), toLinearScalar(b), a);
};
srgbFunction srgb = useLinearColor ? toLinear : passthrough;
ImGui::StyleColorsDark();
ImGuiStyle& style = ImGui::GetStyle();
style.WindowRounding = 0.0f;
style.WindowBorderSize = 0.0f;
style.ColorButtonPosition = ImGuiDir_Right;
style.FrameRounding = 2.0f;
style.FrameBorderSize = 1.0f;
style.GrabRounding = 4.0f;
style.IndentSpacing = 12.0f;
style.Colors[ImGuiCol_WindowBg] = srgb(0.2f, 0.2f, 0.2f, 1.0f);
style.Colors[ImGuiCol_MenuBarBg] = srgb(0.2f, 0.2f, 0.2f, 1.0f);
style.Colors[ImGuiCol_ScrollbarBg] = srgb(0.2f, 0.2f, 0.2f, 1.0f);
style.Colors[ImGuiCol_PopupBg] = srgb(0.135f, 0.135f, 0.135f, 1.0f);
style.Colors[ImGuiCol_Border] = srgb(0.4f, 0.4f, 0.4f, 0.5f);
style.Colors[ImGuiCol_FrameBg] = srgb(0.05f, 0.05f, 0.05f, 0.5f);
// Normal
ImVec4 normal_color = srgb(0.465f, 0.465f, 0.525f, 1.0f);
std::vector<ImGuiCol> to_change_nrm;
to_change_nrm.push_back(ImGuiCol_Header);
to_change_nrm.push_back(ImGuiCol_SliderGrab);
to_change_nrm.push_back(ImGuiCol_Button);
to_change_nrm.push_back(ImGuiCol_CheckMark);
to_change_nrm.push_back(ImGuiCol_ResizeGrip);
to_change_nrm.push_back(ImGuiCol_TextSelectedBg);
to_change_nrm.push_back(ImGuiCol_Separator);
to_change_nrm.push_back(ImGuiCol_FrameBgActive);
for(auto c : to_change_nrm)
{
style.Colors[c] = normal_color;
}
// Active
ImVec4 active_color = srgb(0.365f, 0.365f, 0.425f, 1.0f);
std::vector<ImGuiCol> to_change_act;
to_change_act.push_back(ImGuiCol_HeaderActive);
to_change_act.push_back(ImGuiCol_SliderGrabActive);
to_change_act.push_back(ImGuiCol_ButtonActive);
to_change_act.push_back(ImGuiCol_ResizeGripActive);
to_change_act.push_back(ImGuiCol_SeparatorActive);
for(auto c : to_change_act)
{
style.Colors[c] = active_color;
}
// Hovered
ImVec4 hovered_color = srgb(0.565f, 0.565f, 0.625f, 1.0f);
std::vector<ImGuiCol> to_change_hover;
to_change_hover.push_back(ImGuiCol_HeaderHovered);
to_change_hover.push_back(ImGuiCol_ButtonHovered);
to_change_hover.push_back(ImGuiCol_FrameBgHovered);
to_change_hover.push_back(ImGuiCol_ResizeGripHovered);
to_change_hover.push_back(ImGuiCol_SeparatorHovered);
for(auto c : to_change_hover)
{
style.Colors[c] = hovered_color;
}
style.Colors[ImGuiCol_TitleBgActive] = srgb(0.465f, 0.465f, 0.465f, 1.0f);
style.Colors[ImGuiCol_TitleBg] = srgb(0.125f, 0.125f, 0.125f, 1.0f);
style.Colors[ImGuiCol_Tab] = srgb(0.05f, 0.05f, 0.05f, 0.5f);
style.Colors[ImGuiCol_TabHovered] = srgb(0.465f, 0.495f, 0.525f, 1.0f);
style.Colors[ImGuiCol_TabActive] = srgb(0.282f, 0.290f, 0.302f, 1.0f);
style.Colors[ImGuiCol_ModalWindowDimBg] = srgb(0.465f, 0.465f, 0.465f, 0.350f);
//Colors_ext[ImGuiColExt_Warning] = srgb (1.0f, 0.43f, 0.35f, 1.0f);
ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_PickerHueWheel);
}
//
// Local, return true if the filename exist
//
static bool fileExists(const char* filename)
{
std::ifstream stream;
stream.open(filename);
return stream.is_open();
}
//--------------------------------------------------------------------------------------------------
// Looking for TTF fonts, first on the VULKAN SDK, then Windows default fonts
//
void setFonts(FontMode fontmode)
{
ImGuiIO& io = ImGui::GetIO();
const float high_dpi_scale = getDPIScale();
// Nicer fonts
ImFont* font = nullptr;
if(fontmode == FONT_MONOSPACED_SCALED)
{
if(font == nullptr)
{
const std::string p = R"(C:/Windows/Fonts/consola.ttf)";
if(fileExists(p.c_str()))
font = io.Fonts->AddFontFromFileTTF(p.c_str(), 12.0f * high_dpi_scale);
}
if(font == nullptr)
{
const std::string p = "/usr/share/fonts/truetype/ubuntu/UbuntuMono-R.ttf";
if(fileExists(p.c_str()))
font = io.Fonts->AddFontFromFileTTF(p.c_str(), 12.0f * high_dpi_scale);
}
}
else if(fontmode == FONT_PROPORTIONAL_SCALED)
{
const char* vk_path = getenv("VK_SDK_PATH");
if(vk_path)
{
const std::string p = std::string(vk_path) + R"(/Samples/Layer-Samples/data/FreeSans.ttf)";
if(fileExists(p.c_str()))
font = io.Fonts->AddFontFromFileTTF(p.c_str(), 16.0f * high_dpi_scale);
}
if(font == nullptr)
{
const std::string p = R"(C:/Windows/Fonts/segoeui.ttf)";
if(fileExists(p.c_str()))
font = io.Fonts->AddFontFromFileTTF(p.c_str(), 16.0f * high_dpi_scale);
}
if(font == nullptr)
{
const std::string p = "/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf";
if(fileExists(p.c_str()))
font = io.Fonts->AddFontFromFileTTF(p.c_str(), 16.0f * high_dpi_scale);
}
}
if(font == nullptr)
{
ImFontConfig font_config = ImFontConfig();
font_config.SizePixels = 13.0f * high_dpi_scale; // 13 is the default font size
io.Fonts->AddFontDefault(&font_config);
}
}
void tooltip(const char* description, bool questionMark /*= false*/, float timerThreshold /*= 0.5f*/)
{
bool passTimer = GImGui->HoveredIdTimer >= timerThreshold && GImGui->ActiveIdTimer == 0.0f;
if(questionMark)
{
ImGui::SameLine();
ImGui::TextDisabled("(?)");
passTimer = true;
}
if(ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && passTimer)
{
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(description);
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
}
// ------------------------------------------------------------------------------------------------
namespace {
template <typename TScalar, ImGuiDataType type, uint8_t dim>
bool 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;
}
} // namespace
template <>
bool Control::show_slider_control<float>(float* value, float& min, float& max, const char* format)
{
return show_slider_control_scalar<float, ImGuiDataType_Float, 1>(value, &min, &max, format ? format : "%.3f");
}
template <>
bool Control::show_slider_control<glm::vec2>(glm::vec2* value, glm::vec2& min, glm::vec2& max, const char* format)
{
return show_slider_control_scalar<float, ImGuiDataType_Float, 2>(&value->x, &min.x, &max.x, format ? format : "%.3f");
}
template <>
bool Control::show_slider_control<glm::vec3>(glm::vec3* value, glm::vec3& min, glm::vec3& max, const char* format)
{
return show_slider_control_scalar<float, ImGuiDataType_Float, 3>(&value->x, &min.x, &max.x, format ? format : "%.3f");
}
template <>
bool Control::show_slider_control<glm::vec4>(glm::vec4* value, glm::vec4& min, glm::vec4& max, const char* format)
{
return show_slider_control_scalar<float, ImGuiDataType_Float, 4>(&value->x, &min.x, &max.x, format ? format : "%.3f");
}
template <>
bool Control::show_drag_control<float>(float* value, float speed, float& min, float& max, const char* format)
{
return show_drag_control_scalar<float, ImGuiDataType_Float, 1>(value, speed, &min, &max, format ? format : "%.3f");
}
template <>
bool Control::show_drag_control<glm::vec2>(glm::vec2* value, float speed, glm::vec2& min, glm::vec2& max, const char* format)
{
return show_drag_control_scalar<float, ImGuiDataType_Float, 2>(&value->x, speed, &min.x, &max.x, format ? format : "%.3f");
}
template <>
bool Control::show_drag_control<glm::vec3>(glm::vec3* value, float speed, glm::vec3& min, glm::vec3& max, const char* format)
{
return show_drag_control_scalar<float, ImGuiDataType_Float, 3>(&value->x, speed, &min.x, &max.x, format ? format : "%.3f");
}
template <>
bool Control::show_drag_control<glm::vec4>(glm::vec4* value, float speed, glm::vec4& min, glm::vec4& max, const char* format)
{
return show_drag_control_scalar<float, ImGuiDataType_Float, 4>(&value->x, speed, &min.x, &max.x, format ? format : "%.3f");
}
template <>
bool Control::show_slider_control<int>(int* value, int& min, int& max, const char* format)
{
return show_slider_control_scalar<int, ImGuiDataType_S32, 1>(value, &min, &max, format ? format : "%d");
}
template <>
bool Control::show_slider_control<glm::ivec2>(glm::ivec2* value, glm::ivec2& min, glm::ivec2& max, const char* format)
{
return show_slider_control_scalar<int, ImGuiDataType_S32, 2>(&value->x, &min.x, &max.x, format ? format : "%d");
}
template <>
bool Control::show_slider_control<glm::ivec3>(glm::ivec3* value, glm::ivec3& min, glm::ivec3& max, const char* format)
{
return show_slider_control_scalar<int, ImGuiDataType_S32, 3>(&value->x, &min.x, &max.x, format ? format : "%d");
}
template <>
bool Control::show_slider_control<glm::ivec4>(glm::ivec4* value, glm::ivec4& min, glm::ivec4& max, const char* format)
{
return show_slider_control_scalar<int, ImGuiDataType_S32, 4>(&value->x, &min.x, &max.x, format ? format : "%d");
}
template <>
bool Control::show_drag_control<int>(int* value, float speed, int& min, int& max, const char* format)
{
return show_drag_control_scalar<int, ImGuiDataType_S32, 1>(value, speed, &min, &max, format ? format : "%d");
}
template <>
bool Control::show_drag_control<glm::ivec2>(glm::ivec2* value, float speed, glm::ivec2& min, glm::ivec2& max, const char* format)
{
return show_drag_control_scalar<int, ImGuiDataType_S32, 2>(&value->x, speed, &min.x, &max.x, format ? format : "%d");
}
template <>
bool Control::show_drag_control<glm::ivec3>(glm::ivec3* value, float speed, glm::ivec3& min, glm::ivec3& max, const char* format)
{
return show_drag_control_scalar<int, ImGuiDataType_S32, 3>(&value->x, speed, &min.x, &max.x, format ? format : "%d");
}
template <>
bool Control::show_drag_control<glm::ivec4>(glm::ivec4* value, float speed, glm::ivec4& min, glm::ivec4& max, const char* format)
{
return show_drag_control_scalar<int, ImGuiDataType_S32, 4>(&value->x, speed, &min.x, &max.x, format ? format : "%d");
}
template <>
bool Control::show_slider_control<uint32_t>(uint32_t* value, uint32_t& min, uint32_t& max, const char* format)
{
return show_slider_control_scalar<uint32_t, ImGuiDataType_U32, 1>(value, &min, &max, format ? format : "%d");
}
//
//template <>
//bool Control::show_slider_control<uint32_t_2>(uint32_t_2* value, uint32_t_2& min, uint32_t_2& max, const char* format)
//{
// return show_slider_control_scalar<uint32_t, ImGuiDataType_U32, 2>(&value->x, &min.x, &max.x, format ? format : "%d");
//}
//
//template <>
//bool Control::show_slider_control<uint32_t_3>(uint32_t_3* value, uint32_t_3& min, uint32_t_3& max, const char* format)
//{
// return show_slider_control_scalar<uint32_t, ImGuiDataType_U32, 3>(&value->x, &min.x, &max.x, format ? format : "%d");
//}
//
//template <>
//bool Control::show_slider_control<uint32_t_4>(uint32_t_4* value, uint32_t_4& min, uint32_t_4& max, const char* format)
//{
// return show_slider_control_scalar<uint32_t, ImGuiDataType_U32, 4>(&value->x, &min.x, &max.x, format ? format : "%d");
//}
//
//template <>
//bool Control::show_drag_control<uint32_t>(uint32_t* value, float speed, uint32_t& min, uint32_t& max, const char* format)
//{
// return show_drag_control_scalar<uint32_t, ImGuiDataType_U32, 1>(value, speed, &min, &max, format ? format : "%d");
//}
//
//template <>
//bool Control::show_drag_control<uint32_t_2>(uint32_t_2* value, float speed, uint32_t_2& min, uint32_t_2& max, const char* format)
//{
// return show_drag_control_scalar<uint32_t, ImGuiDataType_U32, 2>(&value->x, speed, &min.x, &max.x, format ? format : "%d");
//}
//
//template <>
//bool Control::show_drag_control<uint32_t_3>(uint32_t_3* value, float speed, uint32_t_3& min, uint32_t_3& max, const char* format)
//{
// return show_drag_control_scalar<uint32_t, ImGuiDataType_U32, 3>(&value->x, speed, &min.x, &max.x, format ? format : "%d");
//}
//
//template <>
//bool Control::show_drag_control<uint32_t_4>(uint32_t_4* value, float speed, uint32_t_4& min, uint32_t_4& max, const char* format)
//{
// return show_drag_control_scalar<uint32_t, ImGuiDataType_U32, 4>(&value->x, speed, &min.x, &max.x, format ? format : "%d");
//}
template <>
bool Control::show_slider_control<size_t>(size_t* value, size_t& min, size_t& max, const char* format)
{
return show_slider_control_scalar<size_t, ImGuiDataType_U64, 1>(value, &min, &max, format ? format : "%d");
}
template <>
bool Control::show_drag_control<size_t>(size_t* value, float speed, size_t& min, size_t& max, const char* format)
{
return show_drag_control_scalar<size_t, ImGuiDataType_U64, 1>(value, speed, &min, &max, format ? format : "%d");
}
// Static member declaration
ImGuiID Panel::dockspaceID{0};
void Panel::Begin(Side side /*= Side::Right*/, float alpha /*= 0.5f*/, char* name /*= nullptr*/)
{
// Keeping the unique ID of the dock space
dockspaceID = ImGui::GetID("DockSpace");
// The dock need a dummy window covering the entire viewport.
ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->WorkPos);
ImGui::SetNextWindowSize(viewport->WorkSize);
ImGui::SetNextWindowViewport(viewport->ID);
// All flags to dummy window
ImGuiWindowFlags host_window_flags = 0;
host_window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize;
host_window_flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking;
host_window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
host_window_flags |= ImGuiWindowFlags_NoBackground;
// Starting dummy window
char label[32];
ImFormatString(label, IM_ARRAYSIZE(label), "DockSpaceViewport_%08X", viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin(label, nullptr, host_window_flags);
ImGui::PopStyleVar(3);
// The central node is transparent, so that when UI is draw after, the image is visible
// Auto Hide Bar, no title of the panel
// Center is not dockable, that is for the scene
ImGuiDockNodeFlags dockspaceFlags = ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_AutoHideTabBar
| ImGuiDockNodeFlags_NoDockingOverCentralNode;
// Default panel/window is name setting
std::string dock_name("Settings");
if(name != nullptr)
dock_name = name;
// Building the splitting of the dock space is done only once
if(!ImGui::DockBuilderGetNode(dockspaceID))
{
ImGui::DockBuilderRemoveNode(dockspaceID);
ImGui::DockBuilderAddNode(dockspaceID, dockspaceFlags | ImGuiDockNodeFlags_DockSpace);
ImGui::DockBuilderSetNodeSize(dockspaceID, viewport->Size);
ImGuiID dock_main_id = dockspaceID;
// Slitting all 4 directions, targetting (320 pixel * DPI) panel width, (180 pixel * DPI) panel height.
const float xRatio = glm::clamp<float>(320.0f * getDPIScale() / viewport->WorkSize[0], 0.01f, 0.499f);
const float yRatio = glm::clamp<float>(180.0f * getDPIScale() / viewport->WorkSize[1], 0.01f, 0.499f);
ImGuiID id_left, id_right, id_up, id_down;
// Note, for right, down panels, we use the n / (1 - n) formula to correctly split the space remaining from the left, up panels.
id_left = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, xRatio, nullptr, &dock_main_id);
id_right = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Right, xRatio / (1 - xRatio), nullptr, &dock_main_id);
id_up = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Up, yRatio, nullptr, &dock_main_id);
id_down = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Down, yRatio / (1 - yRatio), nullptr, &dock_main_id);
ImGui::DockBuilderDockWindow(side == Side::Left ? dock_name.c_str() : "Dock_left", id_left);
ImGui::DockBuilderDockWindow(side == Side::Right ? dock_name.c_str() : "Dock_right", id_right);
ImGui::DockBuilderDockWindow("Dock_up", id_up);
ImGui::DockBuilderDockWindow("Dock_down", id_down);
ImGui::DockBuilderDockWindow("Scene", dock_main_id); // Center
ImGui::DockBuilderFinish(dock_main_id);
}
// Setting the panel to blend with alpha
ImVec4 col = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(col.x, col.y, col.z, alpha));
ImGui::DockSpace(dockspaceID, ImVec2(0.0f, 0.0f), dockspaceFlags);
ImGui::PopStyleColor();
ImGui::End();
// The panel
if(alpha < 1)
ImGui::SetNextWindowBgAlpha(alpha); // For when the panel becomes a floating window
ImGui::Begin(dock_name.c_str());
}
Control::Style Control::style{};
} // namespace ImGuiH
bool ImGuiH::azimuthElevationSliders(glm::vec3& direction, bool negative, bool yIsUp /*=true*/)
{
glm::vec3 normalized_dir = normalize(direction);
if(negative)
{
normalized_dir = -normalized_dir;
}
double azimuth;
double elevation;
const double min_azimuth = -180.0;
const double max_azimuth = 180.0;
const double min_elevation = -90.0;
const double max_elevation = 90.0;
if(yIsUp)
{
azimuth = glm::degrees(atan2(normalized_dir.z, normalized_dir.x));
elevation = glm::degrees(asin(normalized_dir.y));
}
else
{
azimuth = glm::degrees(atan2(normalized_dir.y, normalized_dir.x));
elevation = glm::degrees(asin(normalized_dir.z));
}
bool changed = false;
changed |= PropertyEditor::entry("Azimuth", [&]() {
return ImGui::SliderScalar("Azimuth", ImGuiDataType_Double, &azimuth, &min_azimuth, &max_azimuth, "%.1f deg",
ImGuiSliderFlags_NoRoundToFormat);
});
changed |= PropertyEditor::entry("Elevation", [&]() {
return ImGui::SliderScalar("Elevation", ImGuiDataType_Double, &elevation, &min_elevation, &max_elevation,
"%.1f deg", ImGuiSliderFlags_NoRoundToFormat);
});
if(changed)
{
azimuth = glm::radians(azimuth);
elevation = glm::radians(elevation);
double cos_elevation = cos(elevation);
if(yIsUp)
{
direction.y = static_cast<float>(sin(elevation));
direction.x = static_cast<float>(cos(azimuth) * cos_elevation);
direction.z = static_cast<float>(sin(azimuth) * cos_elevation);
}
else
{
direction.z = static_cast<float>(sin(elevation));
direction.x = static_cast<float>(cos(azimuth) * cos_elevation);
direction.y = static_cast<float>(sin(azimuth) * cos_elevation);
}
if(negative)
{
direction = -direction;
}
}
return changed;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,326 @@
/*
* Copyright (c) 2023, 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) 2023, NVIDIA CORPORATION. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include <imgui.h>
#include "imgui_icon.h"
namespace ImGuiH {
ImFont* g_iconicFont = nullptr;
}
// Forward declaration
const char* getOpenIconicFontCompressedBase85TTF();
void ImGuiH::addIconicFont(float fontSize)
{
if(g_iconicFont == nullptr)
{
ImFontConfig fontConfig;
static uint16_t const range[] = {0xE000, 0xE0DF, 0};
char const* glyphsData = getOpenIconicFontCompressedBase85TTF();
g_iconicFont =
ImGui::GetIO().Fonts->AddFontFromMemoryCompressedBase85TTF(glyphsData, fontSize, &fontConfig, (const ImWchar*)range);
}
}
ImFont* ImGuiH::getIconicFont()
{
return g_iconicFont;
}
// clang-format off
const char* getOpenIconicFontCompressedBase85TTF()
{
// TTF font data for OpenIconic font TTF
// SIL OPEN FONT LICENSE Version 1.1
// https://github.com/iconic/open-iconic/blob/master/FONT-LICENSE
// The MIT License(MIT)
// https://github.com/iconic/open-iconic/blob/master/ICON-LICENSE
//
// Copyright(c) 2014 Waybury
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this softwareand associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright noticeand this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// File: 'open-iconic.ttf' (28028 bytes)
// Exported using binary_to_compressed_c.cpp
static const char openIconic_compressed_data_base85[25385 + 1] =
"7])#######cl1xJ'/###W),##2(V$#Q6>##u@;*>#ovW#&6)=-'OE/1gZn426mL&=1->>#JqEn/aNV=B28=<m_)m<-EZlS._0XGH`$vhL0IbjN=pb:&$jg0Fr9eV5oQQ^RE6WaE%J%/G"
"BgO2(UC72LFAuY%,5LsCDEw5[7)1G`8@Ei*b';9Co/(F9LFS7hr1dD4Eo3R/f@h>#/;pu&V3dW.;h^`IwBAA+k%HL2WF')No6Q<BUs$&94b(8_mQf^c0iR/GMxKghV8C5M9ANDF;),eP"
"s=L%Mv6QwLYcv/GiF#>pfe->#G:%&#P$;-GItJ1kmO31#r$pBABLTsL:+:%/-p5m6#aJ_&FZ;#jL-gF%N55YueXZV$l`+W%G'7*.p+AGM%rs.Lx0KfLwF6##_OB`N1^*bNYU$##.,<6C"
"pYw:#[MW;-$_C2:01XMCH]/F%@oj>-Wmf*%%q8.$F`7@'.B.;6_Du'&hF;;$63Mk+MS:;$l9r&lW+>>#YvEuu=m*.-H####Y4+GMQrus-Su[fLK=6##'DbA#bFw8'tB6##Lhdk%Nb#d)"
"-l68%<a60%*8YY#ksnO(meGH3D@dOgq#Sk#<@KZ+^2bY#t=58.Z42G`R5pP'R#k8&1p^w0%2tM(TDl-$nq@8%uP&<6FMsFrkg%;Qk:MEd]'CJ#q4cQumLuFroPu>C&8FB-#@'$.8mwiL"
"KO2EOR.tB-1N5</<PQ?SQlRfL^5fQMK4X$#8e`##9Q@k(xsFA#cFGH3*/rv-@0steAL@&Mt98'RE:6G`(q1@doX]'R>$hiPchu,MLY5;-]Xo34camA#@YqlLLxQ2MIMh1MB2tuLjV;;$"
"PE)B4-Mc##=(V$#3JRN'GRes$-M:;$eN;hLGSrhL)kL)Nv$Q;-h8Ee#OYLxL/qPW-+l*hcw3Tt:K=/g)&QD.$s@o6<40)V;=6axT1F<U;2OWq;.]BxTR2###;:U;-<3]V$]V@^FJR@%#"
"8l#U7uUO.)Y9kT%1YUV$Yp()3GU1E4m3[5/sgw,*wqq8.5&7C#)#xP'hT%;Q<a&buYwaVaJjAZ#/7:*j>[x@A1P&,;nl8IW*Yn36=b/(G*8FW.KkqrUgt]_uR0.g>Z>,%&$),##50ED#"
";EF&#8EaP]UkIJ1P*nO(@nBD3gp/Ys%*c,/Wp`IhM]^'/B67gL0mM<%9@RS%EE.<$(iNn0&DJ1(*O13(0&Ws%C8I6oF'XMon(BW#Ej5/1#iK#$V@pgL5W=huZXAwR'qAZPH(<?#%G3]-"
"R4B>QW<Q]uIvg_-5Or`S*J);;RmwiL*BauPcOB_-66lKYg,3^#r+TV-jO@mk5ImO($FKx9R:xIQTCfd4XcE,#8EaP]VkIJ1W9m9MqiRdF#jaIhUm9DC)Pb3Fq';dMJ:>GMU'MHME]sFr"
"$#bIh$ErFrtE)Xu*S.GMsFZY#xx'&(-hBD3O4g]uc_.08/tf2`_^7I-PK7I-dn6]-TYwTM9en9M3*-&4%lQDO]H1[MO@YoPM8,,MU#<?#0q3L#r&_B#Gk5`-+XV$^wAI&#MgwGMe/=fM"
"%2rAMwM,i87d0^#(tAh8IG1B#8E_#$uLbA#=2vg)<j8>CoeY9C3[-CuBu[*$#xYr#2w@P#3(CJ#G*2GMlH^v-KV;;?BT_c)lH*20%'%<&DI$,*DiY##=Z[0)jiWI)qV6C#2-#Q/8bCE4"
"d.fZ$aSIWABTgk0;-LD?7L1a#d:+d4.:L^u@Ft%,O27>>$/Ib%@s@8:JrksLPQuP=Wj&2XY'f*.W>%H?Jo*50D@N'6@:&&6Mj;5:[G>',%/5##sIkA#?V)2/9(V$#rA+KWNVB.*0Zc8/"
"_YWF3p;:R/g;2.I07b2CAZAb4hLY['IR(<)[8UCCx5B58Xatm'Dn35&+^L-M&J_P8H79v-P>/B#9]SfLYWY95F5kWo[iJdo7PFa[Nk9:<bmp2<`1g2$^kYhLMbQb[P)^F#&fAguE8*C&"
".'wwB_B(7#1Yu##;PF$/GRes$c^HwBH$nO(gv8s7EPdO(jW4G`Lq#Gr3RgIht28G`]n&=(Vu_?###b9#`KGf%;T<A+&Zu(3%'%<&_T/a+bhV?#+crB#;0l]#P#G:.rkh8.mNv)4kDsI3"
"5;`:%q2Qv$lf/+*u?lD#g48`&/0*>P^-R`E7+R<C0F91p*AM9LpLL=Q3H?.q]PMP0*+_HM3bQ;-`78JL$R]duA'OI)ZFX:CxH#OKvCnf)Gd(APJCZY#)J/-2IdS2^M4v##;Z[0)?sGA#"
"vx:g)Z4jd%lUFb3(ctM(7'$,&H$^>u=NxH#6J8I&tS?.&(w?w%M&CJ#nxkT9HoMs%Ri,#:fs0c7fGA_#kSuIUZ42G`#?#,2'ti21;ix+MG3gF4`cJ,3)_Aj0cI9F*jpB:%cn4[$UJ,G4"
"[uUv-?]d8/hJL:%+SN/2X,n.)1Yl>#4a&m%;Bjo*G6'j0DTnr6>vl[P^Su>#D5UlSjXT)5er^)%7R2=-kL)APv<qa4<IK60tY#c4<q@?PW;e+WLuuEm05Vm&fK(,)nM5N(f%JfL`Lu(3"
"9UsNOj/@tLh]8f3.m(u$Uxor6]]DC8ukm/N3vOG=Mo&vHc2Ii4iJ<cFuP@+`E'flLbcK`$v)$$$PrC$#e28]%/5:$)V'+X$)lNn0vpf8%B:Y)4ZE>V/isSi)XSf_4Kj+5A3/mCW-nm<S"
"`Tr6C?9%0$lKTATZ(N=(=BPEd++wH?Ukx/1U'+&#`,Gj^8%L+**<m`-0]kHZ(LOA#vkh8.7/TF4o=]:/Z(OF3kl-F%3F6eMJGsFrY3Kt7lAkHuNBAJ.3dT3C7Ot2CSeKD?]a;*eB[f%-"
"6K^'RB,aB#FP*2MSiaIhwq$vJZTEV&biqr$07>>#8EQJ(6uv9.nXAouu[XO%s[?>#p?_&#aJxjt^;>_/^1[d+=7B_4s7En3+I@X-vu/+*X0fX-]p*P(&,]]4qio05A@N@,.>T@,(r?5/"
"=KeA?u>kB,pSaIhUT%/2.dOCjA/#2C7CA5//`Nw$*jnA#abDE-)aDE-/MXZ/8N9R//G/JdTa+W-@Isp^Z_?##EdS2^4;5##af[^$/6fX-m'4]-7^9Z-HLQ;-.`6/1xq7t%uR$w#tPR6D"
"CIm,&=G4?Is8K%#fd0'#$sb&,`X2$#p%c00gEW@,QFVC,732m$ic``3t<7f3I.=h>K7)qiFe8V8CwBB#'8:$6-^J6atvN@,P;I80eF&b*l-AU+Q1(`.JLR$&cIqH1Vec<066V9CIBkp%"
",<$'85fK>?uxwV.fE4g)Een2B,R(f)o77<.hp?d)BiaF3iEo8%Z_G)4@O'>.g:S_#OJ,G4oHl2Eq<@12u)Yx#IM6u?b%]=YnAv6Jex/U%ZRQ;-P2>.3hC7),'BW<BvIo#';9l<'$<=8A"
"QI%##SJ%?R#H0%#hj9'#G`gT7LoqZ-$QdR&*oNn0@hmV-+NTj`qc=00vc%H)+,Bf3rQim$VKi9/(o?X-^hDv#@81G`Ig2Au0aYK)uXAA,/Pg;M+q//1gFB,j<34G`R=wP/ji1G`]d`oA"
"Q$1i#`M66CL=6##)CQ$$^0+&#Kqn%#7mJR8@R6C#a?T:%cKff16t'E#E%D'S1lh8.VntD#$be#5@=e/1#6RCsVJ$F<tI`?#Of6'5r0Er:3MBk;spA/1cbg@OmWuI<^'s?#FWS5<3QP-."
"DM_*597on2$&###ZYw4fITr+De1'<3S=>6sG)'J3W#[]4<PsD#%f%s$PL?lL14(f)MCr?#sX.r8aa#K)Nl/BHERX/2ucJ:/mu[:/vBo8%r:#?,h6n5/s39tU3*Y<UZ?85/Q4vr-VSi/:"
"#b<c4-[xo%ls>n&;[qu$0FLS:/.r%,&@ed)$QlvG@5uj)Oi3+3G0Z**uJMk'^@3B6.xl$,eAru51Qkp'&.-,*dLW)3.,RW%EUu^$sZG0(pS4=-JvW@,8PQc4RT8,*IPgf)3;N#5G&MP0"
"U`2p/Qkf34,N_2:o76HN7.7W$]YO@,_NG/(,a'W$%'N50ohYgL_IGG,<;:v-2@`[,=bT]$?/5##'AlY#ID(7#Fww%#QYmq%WqRJ1`tC.33Tlk0[IBN(3pm;%`ni?#A=M8.cc``3eN:;?"
"AWw8@k%#s-00J,;4YIQ0&Z?X('P7G`C)j-&JPciuqvC)*#BU3(UH5/(omLgL0G5gLMQ(]$Y$3$#E@%%#Uqn%#fKb&#Rp8X-@[v(+vuRL(QknW$-xNn0WTP$KC[/Q/:%xC#)vsI33E0N("
"H1:)N]*WZ#1H(0aVHt1':Z/GM%7Q;-%#h3&F'Bq%n1Goub&]:.CCK?d$SF&#T=P#$b,`tL)V[*._1x+MPr@X-Uokl8JJK/)$8h9MO4G^#P^.3M>7(#&S:A2CmEx>dOI5r%:^iqM&_V;:"
"i51g*lBel/a)12'oon60Jn))+`'fJ1`tC.3?l0x5oSEU-a1M9.F)E`#dDp;-JY#<-GOH&%H0B^^<5=E$`Ge,Mu%2&7&bsZ5oYAA,?J<MKYsslWoV[(6DE;&$b/<-&hpE$M$oO07*pcW-"
"j:?NMCQFv-(oHhLL2JG=$,>>#B$k>dKV;;?/addFXh)v#`._f#q%KoMhC?>#[CNJMf:-##-Q-E0rB%;QaVS;-#'e32tYg59ZF;s%BOP]uA_$'Q)uCt/OKkA#Bua1uiRJF-[d'-%q%5kO"
"vfP&#(K6(#=3CR0*_S[#:H%O(xNUA$6*YA#Fr/l'V::8._%NT/-K?C#ued;-1rru$JMQF%'1u.)M[4;dv<[5/H5_T%:='d)/OTF&*j`B4')?N#]_h[,>eq/3l;pNM1'A5/rM?R&*dgAt"
"UZg6&7NF*+?jTx=EIIW-@NSKug0pTV.52G`4CNP&?&3-&?A/'H2Bx9.^1tM(b,sFrjv'Q/'ZedubwtBC/:CB#jI:0C6CBh5E8I/(dcT5',m?T%eee@#.N?C#qw6u68AB-QI`_M:lpZEP"
"sTK^P9?%RPLu6j:kmQEPKF^w'?7%-3KV;;?J(WqgNt#`40<t4SYG,juMZfF-)dGW-^Wk2DNLZY#Kp+)O8pID*IF)?#h:VX1@93/M]VXKM_oX02SkIJ1lF;?#?W$9S$tn=GE$M#$Q#HpL"
"<D%/O(n)U/[[2Q/8a&;Q%a3^#RWU@-nR.K9u?:@IY3E$#;P6*<TH+O4hKr0aWl;;?xpSBZ,K<@I_3EiM(IkB-/RkB-,J'O.kE$`4(9a;%a0x8.>+nDS2BI4=E&=h>]^'Q/Z9V;-YOho."
"<TqA,B/^q'%vGA#gEW@,YG8f3vf%&4O$/i)cn=X(eOnH+s1gF4`XdB,1>MZu]8+-;]kFF#'''k0[gX7IMhIw#Ri3+3Og<X(+gpk9QrD7*416AO7CsEI@cR30R,Gj^]9kT%d`>qpfnQQ%"
"e/0i)(@n5/6a=a;KfZ_#4p#g[oEkCWnP%3:)puH;f3Yn*q=jg.:KVK2HR+8&5R&/1t.b8/mRXR-Oh#L/lYWI)*^iF=t%'Z--nii/S;3,)s<RF4#pMB,1H4Q/m$UfL_qtFb-:jB5l.$vJ"
"2uY<,7;IwPvMc>#^XL@,709JSBlvuQ#sj?>drN+3Pr3&7b&WF=b'4fur>gf)?N=v-=Kjr7wv#K)LVsQNXSlwPPaE<%%H>[01lXI)YAOZ6#FPcMnGkIdt'e'Jh)MUdPco=d`X@>d_d?]/"
"NX.i)Oi3+3AS.n/DEaP]ZkIJ1Bd]N/:2Cv-6I7lLg%J#/Lcp77DsX<h3KKwMOw^A?,C1na;s4T&x6v^]$@O9%(iNn0L#>u-=Fn8%$t)Z$<QJ&OvDrkJbb>a03c7uQOP]'RsxS@B+w<2C"
"$eXw,3d]'R]'^F-LvKl3WU'^#87>##;(V$#QJZ6&0;5##@%Yw2(k^#$BUW:.eAgI*074D#;9ovektS9Ce*loL*uLp.2w@P#gO:1#/s#t-AQWjLA1(?-G-ml$c#[]4bQXA#`&ID*bQs?#"
"(LA8%]CAC#kHSP/GI0P:?fHC#4(=(,wB3r&PC:*31Nw+33Ij,WxMvuJ3)WVRh5l?-mO4Z-Nv4wgwMPT.,GY##TFk3.je'dMT,AX-<<xX-c#g*%k*f%u_I,;Q'rw^$`Pvf;E9Z.6aq#.6"
"d*6.6Xax9.wF5s-6/XjLdX2DdG*B_8%oK^#_.kFdx-Ns->0niLBpID>e9+@nf*,s/aNP;dextBCY?:@-[x%d=<x&#_4:ww^.oQ<_G.4[^n,?2CDOUEn's[j0Kb7G`%K>G2DLKV6dl5D<"
"-W<T'5ek#7T#=i26k/%#>Q@k(4tFA#K;gF4C'+W-`+[^Z2UkD#A.xJ1u3YD#o]d8/@jQv$0/D_&(C=m/H,Bf3Pv@+4*ZKW$RbZV-:'ihLO`6<.`6?8C&)$?\?Yo-x6KZn^43YAA,4&huG"
"]g&i)#VWECnp@A,Be//1?`wA,4lW0R8MnA-kvjS8ln3B,mbX9CkF:@-bI`t-[I?M93+s20jbP;-3xqr$4FG>#l7Fb3js&i)I>Cv-s$gM'OcO4]O^>r%3^lSdq_adth<e##E@%%#6`gT7"
"l1oh(KF.<$^&ACmmE<R%v-sV$95<+MKDlh':E1qLZMCsLb7cB,H]0hL6w//1g0Vk+]gK6/7rC$#0KAN93TkM(GPS8/nr@B=5bOAu&J[n[d2&Z$tCF&#b(p#$5FNP&IeJH*)?[s&N:+A["
"HdF)</.'Z-^I-/:#@-$$xn>V#fnG688MFAu.Zo?NosS9C+Wq-$,kq0Y5rJ&vJao,;#(C7'I]>c*ed^6&Y'fJ1`=K,3cX.f$^#lP9TAu`4/I#H28)qj&s8Kf'clnP'IEPR&-<m`-LH.wI"
"mIAou)ouN'uK,:)0h#h1JL7%#R,Gj^x44.)_xefLa#1i)D>`:%J#G:.]L3]-kDsI3d,ID*dkS9C(8_33L#v20_gB_u(%x)$>m;U)Y12mLS2sFr,bdt(LF.[B_Tr22kHe##IXI%#.^Q(#"
"Z=sd2PC:J)KF.<$U^nVoVB:=%iZeF4uP2)3i%7V87:Y)4/D-HN<&iM'Fvnb0O)cB,4&Jw#R>iO'Q'4$6Ga7#%-DDJ:j'VhLirMP0v$C>$lD.gL*kC80AYlN9qjTn/3`($#Fg99%OT(]$"
"K1>u%$CZV-1q//13RgIh(17I$E/<v>8QX]u.d*X8AI]v$d>f;%XtRb%]:75)Vi-]-(^&@93_V;-YG^e6J7ws-f$L-MDMYGM_0SX-(=.p.n5UQ/D/)GuI/15AV?96/3BZ=Yxo[F:HQXLs"
"Yw2$#/h>%.HvC;?'*Zg)n.rv-dh.T%ve#`4lYWI)QNv)4%ZFW#o(p^[2I(I#-l*#7Y<2.Is;#eoQOuf[c%q=?;5T[8`6gM=>-Q2iiO2G`K[VV$7Rjl&FnfL))?[s&bvY3'CI[s$Y-oJ1"
"E=R<_2pDs%*$[g)mBmGMN?H)47p&;Q;GB0uwRE2CsIkA#NKX_$>aKs-8(P6MjL1_%D&###aMwFrsqou5P']m,'fNn0]_OF3JIV@,M$g://]v.4i'mG*e4B>,j^Aj0<kY)4ax;9/Tn.i)"
"RJ))3wc``3onXj1:s?^4_Bq:d$PJ@-xq=K(NljE*b<bc2(3D1;8uO]uFf<C4](e3*4`[?AuS-*3;1(p78]F.3C;TBR6%>H&Ul68%=jQK%r$'et1-;hLRZ/@#$#bIhP$+W-U9:wpx215M"
"t'918/Tbk4tJ*B.^,QJ(29'.$Xd`#$XoI08&s9B#Lu@P#h(>(.p9_hLxT?R&=.:%/bfQv$&tc05&1=]-6C_B#6G<GdT4Nm#Dvu8.cYxi=+'a216a.c%f#ak=dbI<$RnsK)_;W.3^J)B#"
"=;`C&I0@)KG(@$g]s9wgaGA*n2Lh'#%&>uulY@)$U:r$#7Yvh-t'YKj*$&],+3j'%j7o]4apCg%q]WF3;X^:/oNv)4QdPw-L%O;9`H[n[ww:v/,FeB,tKO@,ep3$6vqf5'nBI(R>Nuf["
"4:#mFxCEZuG5EC=S*cB,vj%.MMJ$291i9^#q1:kFNMY>#=5^s%2H`hLf%f^$2om8.]^C8#Qnh9VLVUV$i8SC#(<0/1$J@;?p/W;-jWGouR7Fv/Pb./1<)sFr^n-q$NsLB:K(Hg)$%nhM"
"0U.Pd/A:.$b)g;-w#Sv&R+no%:x,W%6oiu$/%@p./6tM(.@wq$tTQ^#+r0Pds@O]u@#pG&i435&<U35&Q/bT%-Duu#,.aV$6<Xq.aXa>.>c5g)NEFs-_gPS@:;xp&FI2Pd=1.AMUV7>S"
"un$W`w5V9CQ7T&#h-7m%qnrr$>i7@%PBBN*#DRv$t0;W-wod;%m^D.3Coh8._V_Z->-w)4D.7x67gurBK[3Ib(/&,;MV0?)<-OBkvmYf>e_VX^m'1,)0D>;+1H:q#`63.5($(U1roT3<"
"IA]C@PGd2CKoxEI.*nMjpg&%#6x5U7n+oh(qe)<-]VsR'e)ahLKZP8.$ed19p_v;-Bviu$vj4N9G'Z'?>IKC-#_@S/j;1g:?@(XS<U?a$K5>_?tor#($o@g:1gF>d_[i5r_A-$$LYu##"
"FdS2^Crl##9H%O(J%1N(m`0i)x1tM(Zk_a4U&>;?`04]<<Lt2CP#=2C*S+RNPjo@S(9a=#95>%&g&p%#&dZ(#:efX.mqD$#*crB#AY>v,YLU8.Od5,2]AtJ:cT3T@D&Du$@]WF3cb(f)"
"0Zc8/xS#8RJ40:95nkp%;Omb>igZKN>P9L(44'k1K_K6MYsslW)aog(:_i[,p)IG=X13G`O39Z70$Qn&D.4x71Y=)-G@-u6KU4x-aSDe$)D([,.rbi(jEelL+iZY#kSuIU^CMc`j*io."
".Y=W.9offL&(tu5kiE.3[Z*G4AMbI)#R6.Mp?Uv-&cUNtQ/TP/>;gF4See@#v2=v#RD.l'ncgQK=SHt7prgf)3lW`uE=AX#TVGe)B[0DLu33G`))[#8xf8qJSKOW',rKJ)=CEL#FRGE7"
"tql?,Du5]MwY1f+TRQ##XmWF.4MG##Z.n#nW(hh&fK8<-]Cex-Ap/kLfQAa+pNq12nS.d)a5eD*jE7f3EsmC#2RQv$KVM_&:OQ^+IL>n0/[?U&FF76/5vai26bZIMaWPv.5Kuw'#h^>$"
"jP$ND.cTs9FnNp1ZN%q/7kgv7[RiXu>['jC<;v<-OH(5'YsrQN,GkWSF&oO(:76g).CwX-DrS9CYI[i.51Gou46E/%<gOSc'cWj&%ZI*-PX=F3>GVD3<Qwb4-Pu>Cnn(ju%'DC8oA5>d"
"Kn6mA^I6Z$(rQ-HmA4K3,0fX-H_$gL*]d8/sRh`.N-v7$hXWY$+w<2C)+r772=hf)qx8;-UsgY>FPaJ2tKZt.'fNn0=R(f)YsHd)7>1p$V>%&4&=(u(7_Aj0Y$n;'&QVA.OGg+4GF[?\?"
"]g8P133'#*)LwLM@/2&74jj6/id1*3URQ;-8khh;$p8?,%>2e;pFNPM&+*#7;Lo0:X(Z<-8,pL:8PQv$2+:wg%&>s6t++E*$C;W-NZAX-ia&;QK=S?SF5nNSkko@SbCNJMYI(q7.uR_8"
"`4`v#(iNn0)+niM^uQ2MgN&;Qi2_$9,lO]uKmWnLQE%q&=M.>>6oA9=AZ$B=`82t.$5K+*X:Ls-4h&K1`1[s$q[2Q/$8^F*rHNh#S5WF3NP,G4hO=j1HZU&$ZktD#5>PjL=`n]4pqi?#"
"H&,J*/]&E#AIwZ&#s<h<PaM4'*4$O(FEPN'm4#(+:LL;&E9gq%/i1E4>E(1;>'SD*kZdh2`Dxu,mpP3',XTE#8O*t$5`tFb2_Nv#nI3p%YPw)*k9ob%$XA=1ruVs%a7kI)@=.<$kGiE#"
"N4GD4RSE**[haR8;0h:%71#m'N1gT&f;m7&Ze7W$TmLg(ZHj@&W+2?#Mp:4'M9=x#%Sdi&ZFNP&DTo&Cft9B#jPD<%TbsJ1'<1^#_<m=Ga:)I#h_/rB.xCT.dC)I#Ulu8.3.`$#v_B^#"
";r_':cKQYS,v:T/(N#,28YR]4He,87UH^#.f1))5EK[L2%f8q/Z)l>-:CHc*qsJ<-hHFg$?:fpg2-RAOi-5.Vx^WR%.eY9C%mD('cFM<-;^TV-<KtglJFlW?62vG*&)t.dDjZ-d<pW,M"
"jcNK:SK4Au9^)t-=1.AM2k^KOVj*J-hVOJ-S@jM'''lA#&K-Z$3.5F%F9OW-n=.`&i<F]uSwY<-Ot/gL3S$m8N(:7'^CPV-XK+n,gBFt$)lNn0k?8m85dTN(]SWF3]+:]?q0PCHuG^[#"
"Tvgf)QQR@,ZD;;?g$a?u3*&v5P]sD3U%-w)tWS@#@nh(*iip[krQSQ,VHf;-Dwd]/k(@D3LXDT+['uJ1.;Rv$BxF)4TMrB#`i.@'3)4I)5Djc)4gE.3jLBk$dGG)4[V)60B>'i)56tB,"
"M(w'&xOO]uW?DX-2'B(&-C$_#wkjf)-pQ@,2W>W-u/KF%3.(B#j_n.M8=kf-XLL-Q%]L-QbiT-Q8n@.*u^Op.jAqB#aU%LGDF,gL>fxd;]Fl)4fc5bsDdQ]/$'Cn)'<)iHRp)IHIgdsL"
"T;I]MOjNg=aupbu,6^gL5D6wJ>%r;-Jr+G-Lq;Y0`qIJ1tveF4Y0%.MiMC:%HeolWDgC5/^%AluJ<cwLI[qI=j@[#6$,vx=Fh6gLY&_mA2VLv%*<x9._HF:.>T&;Q7CNEdqDDe#b?^6$"
"w71Pd(l*3ia0E$#JepeF9HAX-*V3D#?Kaa4o4P;-uYZ9Cj)?2COqf5/ZQdduCLk%Mew%bO>x2$#u'B?.CfP##$#d;-=5ZX$[aoI*]kbF3]%p-)F53.)62m'=WlK#$U=#-ML(i%MOA#I#"
"nPj#$A4f:d&?-9.;M7%#o&U'#,]ZZ-^L)$#<Z[0)lw6u6J]B.*-<Tv-*Lj?#f1ie3CmQv$^^D.3k=8C#tBo8%gYiM1'cl>du@cI3,o(NE#Po'&^)T1Mf;*i29Q4;7c'(E#ANp;-PwNn/"
"MgAa5balSVO>.Q/P0_C6AWae36C(C&G/5###oKB#,DF&#'EEjL?YqU%Y]J3%;cLs-aSV)3ig'u$?S<+3FLRLMIU8f3w<7f3owsM0`0S(6]u&)GiLY<Bm@j;7c4.1MqeP;-*[9%6wP%a6"
"3::e)[*%u67N[v.(q3HMikT]82^03(9W?K%Q93-v7*7g).$$E3m>sI3NY&E#Z%#Gr1qD)Wv^m).Hx&;%Nx<2CFm70C?\?1(q2:d*7#Xc2:Ti>g)6sLlMl0.q$e6AwY2^P[PAa;;?enPE>"
"(cS,&01em8Q``'8(8F`S<3KV;0M<dteWj$#OdS2^B=qp7p-m<.3,Jd)]_G)4_V8f37'Hd2Frj2RMR7m'nOgIhWwZ>#S)oShe#s%gvs4/C:12,2>Bfq1%%).M%DYcMWg;;?[75;-CdU&,"
",h:_A2bAW-HXKZf?dnO(/,-J*AeL`$/DZ;$x,rFrXkuT:X^d4C`,LEdTCYG,qA6FS2=hf)qq[d#Gi<?8jPC*6Z[?##;(V$#PDQ6&5a_m/X36H3VF3]-`(`5/$tn=GUM6qL=LL?d,:-$$"
"&.kFd>VAt9<NN)#'/###h=PxtIMMJ(.`JV6+522'PUX[7T=(/)F?r`$l]B.*1Wj;%I/X'%SSK5Bm;pY(uf]Y,LVZ9/eIfm0+LTfLvO5J*2u%T.jAqB#1Il805cVQ&q>6R&e^B<B0N&;Q"
":s,W-/Snx'@^s'+^I%s$sMA@,g7,_+Br$W$+'&gL6hM@,b.$$J@%uAB_m$%'N3c%'Z:U>,jvOA#b`vn&JCf*)R)cB,]7s^oQ>uu#eBF&#Od0'#oLXs50>cQ1=G5##=Z[0)okY)4:Z]F4"
"(cK+*x_OcM&t'E#x.<9/8C)X-fpPA[Rp7jL&c6lL#0E.35MI[')J+gL2a+c43Fa$72r.DC*Pr+M>Up_4X(';S+RM@,FQTA#&$_f+Q6@iL^^Q-3cF@q%J(oQ-5W:V%sa;e33a_`5a<=DC"
"tT3>dE-lV$8wMfW*+n@,OuH6S'8_],G(hKMb#<e3Z],@#.TW'8kT6%-3gc40udp(PAR&##%)###QwqjtEUai0*jTS%.E-x%)iNn0mf4K0K_`8.>;gF4`m]Ku[+'b?fo+G4X_giBJ[lS/"
"ctMm'P[cQsYx6W-VU>qrSK;^#X]J$pm2>w&]3FX$jt$A,bs(k'.e3.NO6Q$,UG(.3AV)d*m0V@,kY[-).UwHN<CUf)-_`3t0<R($@uvE2,W(v#8=nS%-bi[,;uL;$``F'oS#Gd3=F=gL"
"2%52(8UYgLL8,LN5`SP':,:W-6wpgucHh@.Jn@X-sBj;-9a^s$J)Z20$#bIhFH*;Qu5p'&FKEx'00O$MGoU3C37PF%MSl##./Sj0Z42G`5FNP&M,@D*fh18.(N#,252Tm(u4eW.D[Zc*"
"j,Qn&9SG##@Q@k(#dCp.pN7g)Mkgb&T)cB,@qa>d$jV=YDmZW-?kWX1RLg+uKI%>Qx$EM-+61d/IIvu#3wFQ#K%9g1oqP;-K#adun,f%ut1c98B[X&#-,K9iW*+T.ElY##>]&(10ImO("
"ij@+4auRv$#@F,4Mk5.I=Jr@Cn=#AdU_iM=M&CJ#':Po(P1T9ChDUO&9&:Xo)sLh$+6)Z-hfDu$kDjB#'1Lc5wo&vHYA,C8v4(_5-+7f=-YV(ZW4a?>^qD<%=5gF4c<lA><J<?Q<.R?S"
"%-/Q#@<^;-WiE30g.(bH/Dv+Kj/w-$br^+>?1DR<RA?fX39MhLO:pk2]BF:.&cGg)m`0i)2^X2COZo6<rOt:C(23qL;A]/0pSaIhFY?DCP5T;->$u$%&WO;-BjTP&B[RS%RmrZ#(iNn0"
"q3P,MRT8f3Y8mA#r8oQWXg^R/eJ0Pd#gL*6p)M*MM4N*MZNt1'-2O$Mq&T9C[PbIh3^Ps-L5k+MHH6uulhP;-`U18.>gQ#-%ua-?=7H,*jPe)*ow3c%=CAC#DOi8.'U&@#J29f30d%H)"
"*t%s--sxs$7)(=6@rFq&6l<d;2d8T#eGv+RrkU/$NYwi>pUHh,^26O07+20C1#kjLC^GrHoV_'R;FG&#,0,t-#L$iL<N]L(LeCH-#7w&/C7r?#41X<8W<orsneKD?A<B30+-2QC)Aa%$"
"St005lZxF-MaI6K6?TrJV<2.I#S.3VB8xiL7Tn##a^''#I`gT7IS($-cEk9%*oNn0>0;hLs,Jd))afF4EZ$@'vKCo0o+`^#D.Cv$X>a(66hQ;-XV>=lFcof)f$Y=Yo8Yg$LiY,Kh=+9/"
"DX=r/e0P[$snP;-f.vS%mH-Zt<E&;QK.xH,.n4&7tS2q/c=-a5)[Is&A/#2C'7O2CI+M3M_uT2^SJx/&>ImO(.@cS#GJ(p#Enk$/uw(##TQj-$cehZfWq;?#UZZ;?n_oF.Roqr$W`qP8"
"Q@dV[Q]Th#2B0[M^eDJ'$Gb;?pFqB#W@.@#$'CJ#HSGJ?e]O&#b=Pk%Wg@?$*;YY#S1U;0e%l0M+b;;?Vs9:2rvKLNOUZ##EtTs'1-;hL6aA@#`r(kkv9L(ARc`:2mLm]ODknO($o%2'"
";6jl?&G4Aucr?T-d$EE%4ctM(Ev>X$;FV@.PV?B=b/'Z-cpfZ0]Xj=.1(^S#FBlo>q/#+%/6B2#T++,MkeP;-:ar;?EdkfGUJK_%/XSHc91lQMS'wIU@WK`apr.;6;hV/2;s,t$w]B.*"
"]-'u$Un0a#Mq?jBP2QK)4(7]6gH7lL$KMG)sN_hLREfX-u3YD#aUp+M=)/)*HH3$6puxl1ULY>#=<a*cOrcB,$_I70_2n+6$N&+QOg?O$Tt5;dKJDW-8biV-=xG>,jBhb=>?l132=hf)"
"'RMx(q@K60,YpBX;s13aJ:?>#gxqs--d0)<J#tY-CgrA#/a3b$M6Ts-?\?<jLP.1Z-OYC]&6_Aj0(_JJ.oNv)4V%%aED-PEt?JxCu?W;3CetKJ)s>W]$h)*e)adwKP$.5nWHgsKPm7m=G"
"sOY-QUt'3$AB(7#,Fil%BY6W-j9uBA#f+D#0sqYXG-Xpf%SN/Tc#Pv7CU`UI%k9xLI%###wF5s-e/O$MrQrhLhn(-M3uID*ov@+4Nf,lL$k3Q/UP5W-jkx<(v:+5A7uMvL/xocMa:2G`"
"-DR'OK?CU9o][A,%fQ2(4b/O(0Zc8/HjE.3%q]8%MM?O(3dO;-SuMD?=[o[u.26(&io3=?N_W;-=KeA?:3js%#5L^uLJ76/%3uM'OKIIMBt;fUld%_]U8JwB8@bqg?4aY$+%^=7Kwr?#"
"0S<+3MPsD#+IA8%J#ID*,+:^#Ka`6N_j5D#PRC16bR2o.W;LpA&H$9%KlKAt%1@A,+lbOoi0%f3hgv;Hq9<Cm+90)*X*3&6q/Qn.XLi5B'EqS%I>XDs#)LA,/0_LpO6*f3W4OrLMhgKm"
"/2`$#>@<9..Sl##/It/1jH3Q/fU:8.&N1Dd/NYS._J@5$Ii5<-+2)2'$RCG)lNEM0%'%<&A+Cf)EcG##+crB#%1]5/?29f3^*6,MgTdP.e4B>,7u>X(>&<+3G@oT%5H8f3k?jv6,:tG+"
"o;An1//DH?Rk)d#S>1?dn3A+`O#N$.+4J30c#HMXRpRQ0`o=8CQr,[H7uw97%3)6:wAi$>'vus-fR(dOg.:a<mUve43w?AOMgSV6`xn<:](&8q9F7.uASS;-W<ZGRJnu?CPn+2=[KA(H"
"2lMY$>tqFrSO9MB:s79&b7m3'h`)fO=2:Z-F9B+%RS/H2oW&B)%RM1)6kjNL]PMP0^SY%&dfv1'GZWCj$kuY>fx;[$Hm$)*L4:$)G.Vv#n6+T.-E?A4)T9s$4fMB#?3Gg19[oC#^RS[u"
"4rpl/Zw2mQi;8/(LrsGhfhQ;-D7_J#11F7)tZp(fW?A3g2IK9JCao3%C;Dk#ID(7#UEaP]&(;]/2[9&$,CTm/_R(f).73Q/W:`e$@DUv-?]d8/[Xxw#iN%;Q2=hf)t_6$6[1+51[(J80"
"lL&s$X#c]#bcHs-I;:-<h)r50F@dY,q@M<-j6We$Z,g;-6-0b%NRg(Q%G%C#Jr0[%-'k=GYS`m$Ygh>-(7,h%6ImO(4`%b/XRG)4:AK?dU]Op@P@Wt(29:N0XDF&#D-4&#@Y:l95Y^-6"
".p[D*MkJv$&O(02qNh'HVIR8/OM>c4o6T9Ci#:9AM68p3/bc;/wwpouVuP$7U2t(G<h4(-91`'Rlg22D(q5(-t:'`QM7`P=+WvNFLNPN09@%%#Q,Gj^..GgL?7^J1<5'eZ1Ar`FXq*,4"
"IjTv-`aoI*?/=>d$tn=GR)Bq%.sV=Yp/R['?T3>dw]I7nr2</&fdc;d+J*F@-__$Bpi5<-92SB.;%'h(]:nW-vP8-u>)0i)<W9.$nj@e#RS`du%k=DC$fB^#QgINMEaIa$J),##^9)8$"
"U<_+mic.)*W+]]4[*QZ%JRo8%Tc:Z#Ei?<.V,ZA#]%-29Es.[53B'EY3d$=/Xac_=V8w@HP/,j':Vh3?;a5WRYG4=CG_(T:IZ1aue)q%[6k:d2+5t9Dwfx9D1[sr$N.2KEKPB.*[hi?#"
"#,NF357[x6[bZN))c7C#S'Ig)dk=X(pu8f31m@d)nSID*-%1N(5Llj1(7U`>#f[w#*CAu$l%W4'AWio0lV?;%*HW8$rN$ENF%mW-1h;N,fW2^#Soq68;vv$v`=At#a(U'#fdS2^7XW$#"
";Z[0)1RL9Mc%g<$D.Y)4#,]]4(R7f3_Jp5AYFn8%CP,G4L#G:.l@h&6]?wG*^Tbp%Pp$[-YJ.d)@LR8%gTl&>bdh:%Zj::86k@e#S0P%?ZhN_,$(Q,*@:Z(+ZhEC,U;rG)cxN**5'A*+"
"ZfrL1G9=:8ej_<9w5-%-H?[i96V22'dA4P;Ar3X.oCt$%4dChN,ZGg1AG+jLs:Rv$0DHb%/W8f3w<uD#.B8$&wLU*N`$o>QO(g^$/N]O(*X/A,7PGk<Lw(A5IrdM=&Qg1:>l*B,91Jh_"
"1rtA#n-[F69bi=-)Y8#%%9ve<EHDr&YoBj2-BHM;`ATcGLVA>,mE&#*;a%=d]d`oA&a:%I-rY<-YwhQ8mAT;.>v:Q/xrZ&4nQXA#mJvJ1'LA8%55%&4iCAC#o4/B7@N<$6GM1Ddh10H["
"&..Pd1MBDXp&r3C*cF1$:40@-C3A/2e:88SWq,IfiBj*%Isn<a<BsYad+m8])0KfL2=LP8ew^#$J$ok9NHSj0`N<P(p@7<.vu/+*T5Du$IU13(8E%8W;]dIh4hC#K+.VU?.MU7XtuW;-"
"9id;@U&@#MDvEW#:,Wl=wC*-?7huM(.1wX-MxG>#Ak<PXnK[h#OUvcMK''P#vUJM&#ITa+:.OF3)DYo.ZcRUdb*(m8cKW9`%0S;-[PE`N<dic)Dqa?#aZ'r.gUMW-<SEL5:)ap^]q)$#"
"6o/k$/k#R&Xw[J1@SW@$.kE:.K?#<.BRKfLIVOjLrND-*ThWO#leFHdT+U#X7uj>dodG%I9mqlKtE`?W(H0IV0:(Lf+hB'IWR#S&#C(,)b[18.%'%<&5ckI)A]G##=Z[0)<L:_ALR^fL"
"fEPA#d?8d3W6[UT..;3a27wBC(DeLJ(8;=C7@Ga[$Cw:0Gu8Z$&feCa2*n_VTF^*nL'sGCo^5`HJ-(k&9D@j0SEaP]ckIJ1M]J,3x/_5/9`p>,VCs?>/>uo1NXFb329$C#mS=`uM*cB,"
"oWG80=W3$6DC5-2hX6#6lrfs)n$wBt[_:$624L^#XX=)#o_d;-L=2e$'a8>,PkVT+BSYY#39Ks-5seC>h/>)47?v20BsZ&4vBo8%6VA1;`I,;Q'_>]u.ob318q.PdYkZ:([v'k#Ohv1'"
"Fd<U)8:@5/U'xU.c_$<SHQ6=&&'grKV$WZ#fS6g3BgdC#D,_^usT6^gWNGFdt=(nu?+hOdteMGZh$,qKfW/%#/S2I.ZXM?#*gJD#6hX=$kpB:%X:Ls-i.<9/g]7Y.c;R)*qO`S%FpD9)"
"kmTM')>wY7el.[7?R:F#C(ZCj^0v.3r^>r%xkkf,8>b:=AW[128wZ7-eaCo@OWe>^DKd#-sOQ-H7q/W-,hrR8[,lA#G[+Y$)?PF%Q0'#A[/_#$0CpFCUxT<hh&eA#(JU5'$fTB#agfI*"
"66;hLQm`a4=dT3C_Z3>dg2N#$Df(T.3tuFrpD5T'd:35&CEcf(%'%<&[WpQ&Nhi2M@mnO(cFGH3L#G:.#&%Q/1r7DCUeRUdRY0[K20Biu`4+Aux$jrF;,$eko_g1<++pE@#DSq;;S?>d"
"DA-D-FdC>8lpBmVREIU7IKh;I@^2<%-xNn0,Mp+'$,-J*)vsI3>I9]$,'30;PcD218NY7I_iq&5(p(;QX4;f$00O$M$R>L8k-=2C:L3>dw8_+$R8La<6w9B#r]i,)Lx$m/:j8>CZkTIC"
"o:gV-ks*6L5[.Pdo_A8#.+SaExR?T.jd0'#4pm(#T%T*#(pkn8bVTf3w4eW.6itI)KF.<$/(On0xJ0s$-Q'<-)noH;HBIT*#7=.-F3OZ0O)cB,VR,W-lS#+%)[txW+=2.Itn@&FBD4Au"
"qCxfM@fP;-tNpA,tC`e$$jE_&uh1vH?JreMiMrtOo3NK:Som_#XDF&#E@%%#S,Gj^pVr0(.j^s$uS52=34pL($A(s-R&SF4#':Z'WO2?u^GwM0GbN?k(C.>6:0niLPJ=jLiK/[#oxBI;"
"9rS,3:wd3`g-I##BdS2^4D>##9H%O(03HA47FUhL&5oC#D6&tu&VZTIcm'^uY+'U#3CU9'Gs0&=S8-?\?1435&[,mQWh0bT%bvD#$@@.H3Q7qo.GXuS/L-YG%<j5/1wBw]-LW1F%a,bfu"
"jn4[$rRcIhWQO>d:ThxM>rmh'*,pu,%'%<&eWAW-]VT49m:ge?M$nDNLt2.(Aa2.IRjjrq@)#)>/Gj.(Vf9vHuDPn*w9m1<BC$_#>[u##EdS2^wVvw$t4e;%VRG)48'lA#DX_a4%#q3C"
"]DwFrwxxa#_Em=G8PL6MLnx,VR#O=(F`Aq^c93$#IL7%#`bkh(Z`#&)&*nD3BnPu%F*kp%^/p5S'9]9:KO1/1U)6<-+].:.tW`-#6,NH2KEaP]_kIJ1@16g)w*gm0dG`9&wgx9..FE:."
"_r0%6kUlf)L6#53kw*B0_CQ;-NKxCug1uV$T$n#6PEpOMB[vY#'`87.qjQiLX5P=$Tr[fL<knO(W=K,3he9:@bb5g)-24m$=FS_#MGY2CZ.15A,m7I,Vqd[,;O&7/=,xr6Vs$21QMp=G"
"(6B]uOTM@,d*,5T$v`K(tj;TD38t9D/9s9D,D5;-OG[#-.Y]fLB?@A4;'PA#YiWI)N_h&]cZs?#)CuM(xQ:a#2aTb%@rJ>(%seh(5qUJ)v>^c30C%X-cl^',K<Y`uak@e#8Z;v#Qts3T"
"aCOC,/U]w:sH&;Q+^>[J6_r=G:1PG-[Hof$9REM0`:t;.2VJfLG7SX-1(:+*]R?tJec]+4wVp.*4+h$[N_j=.(0rC$G,^w6%4^S#QFD.*[h3LDVCi2Rva)re&YiPZSMuFrt^#l'**Lq%"
"S>4G`HYET%41*I-1O/72D1IE5704mQ-Xuw#@awX)0@lm'L?^<8mIgZf^gO9%)O$]%I>mA#2K4L#5m;;?[iZ=YBul=G$jV=YA<pHS,S`du%8V9CsdVU%OYl;-jf[m-XaI_8l]B.*.eBN0"
"oPVD3;S<+3*akBO<5:Z-tUXEeq645Cu3`19'gM*6o(PP0F7I20%RM1)UZC80bmOh#]PbIhL?R@,`f*5AIuN#-U=*OWaEj$#Xq$*..C(-M]-'CO3@Bj0LNv)4]MUM73vOG=6UfM=`ZW>C"
"$ed19avj=GAWD80$tn=G`9#j=w[)_f-u13Vm^3$#^Kb&#B`gT7?v+B,^wnW$*oNn0]K)%%Zd*P(d+8`&3,7`&5ptNFd/<-&vX0^#0W5W-6q3+%TEKNrWtvYuT(;?#HYYF%mZ&ZulT7U."
"a0j=GTu_H-'NA[9j=%a+0QSn0ji),)$S'41TH8&,2xg%%_c^-Q2i:8.AKwUMe8nqMST3F.?FYI)Q#P)4XIVp.`8i;dd*3I$PUSCMbV0K-Ag=,/xZ6/&n=ge$f>s-&/>i,MfER$&V)f;d"
"'41@'_imp%Pcd/:q;h^#87>##9hVr7Fa,[-njTq%-M:;$eN;hLq[3r725Ld4ukP;-n-cp0,s$&4oLc'&*rB^#?PH$8^+Na,AX,hL(-[pgPGE,#PSuD%`=K,39sZ]4]52T/&@Rq$rUFb3"
",asFr<o.qL0whf)a$M@,cBYG,2>bduvuMvLjmS@#+.,59+SJ=%dhLS._,$h.^LVZ#)lNn0-%.+5V`v[-kT&],Enn8%O=Y)4wHuD#v.<9/tgOU%U`G>#'6h/N7l5/1PcvsK*=4s-hN<r7"
"'1Xg*L]rP0UXtUI7`d4Cd>O'gL/m/C2$).P`YP/CTCRr8RMB58uVZ?\?Mn78%:CRS%366Q8]`O_J>,guC(pH@9SZK<-Oe/;%>H&7WT4T&#HYTd'&fB9rp`UWf<;DD3/?ID*;^9Z-f(]]4"
"s@&d)hJu-$6LhF=V765/.?Gu>V465/F`R9C-1niLpXn'=D(`4)-U%Zu33GouI(*kNmv2*M$tv##'X6iL%pO.)jH_Lj+wn5/Ak3Q/`W]I*=sG,*Z96a3mtIpYRxed]@86M?DrZ4:Tw/I4"
"(cKs$l=i@@=R[euf5*523C)-::KF&#qt)W-1;b9D5DPV-T9om,NcYY#TCTfL<,OF3*wRa$)+&s$rTZd3b@u)4+Rw.:XJa`#0LQ3B9978R9j._u?5?,,,i:8..pK+`Y+Xj$::h0,CjTJD"
"P*U@k$1P]>;>WJ2Gk<MuPQvt$N&###8&M/(N+FD*pk8K1;F9a#AM>Z,r2Gg1Y5#kkf^#;/+gOxtq6:].3fWiCR;dc.J>V@uP<[:t9KCR#Gv8h=Ed[x+:[K#Qw3l-,SUY$dp2QD-*/Zp%"
"7L[Q''fNn0RsMhL(mUD34WFL#jL3]-``1I3(Ci;nJj+ru$,_^u=W/+%JTW;-9Ib2C(q0Pd[Ln;-`b(g@`CrT/am45&O[l^,tGn,Z.s_A*[L2*m%],G4$L[+4p/0i)NIHg))wZ)4Gt%s-"
"*^JW$<Utn%V:.%@J@?Yc,RhVD;>tK(m-2hPZwO3C#-grec;uC$K8wp@aST/D420kL2Hf'R/ogJ*v:P^?f,Q_/P%5p@Qk(T/uxmQWl/]VW,c5g)QO*e.k*_^uV9RH.jZA>#ma;C#aOk9&"
"I5ts'alVP&/.=X(,3cv*0NcT.m=V0$V_`=-Wwnf$P####'>uu#4#Z3#v6QtLUOvu#AbW3M1ilS.&?F&#D+2t-[.JfLf:qV-6680-XEM_&5xkA#R&+>.F.$##ejZF%Bk60Hi^u-$&m_#C"
"NpaR3U)'#_rvg=MHu)?7]f`;Y8NkfMB8O?R4IsJ%JImQWHqPF%5lQ78Kqe`];>%RP@q44N$en]-3I^l4[1ivPx:K-MF1:x&mp`gL)7GcMZaA%#r&3aaaBV,#4e53#wDmV#*OGF#fejL#"
"Q$bT#*1rZ#,8W`#/B=e#0@gi#5Iuu#JBr($iT<5$&IR<$ULaH$jx%T$Vl;[$uAXa$Lvc3%R)=&%*TT:%tJ%H%bCUn%Qj,V%+XAa%+%wg%8/]l%<<Bq%qrp#&HjuJ&8B[<&Bc9F&bGgQ&"
"VZ3X&('i_&H#dn&KLSx&]ieH'NXD<'ZogB'K*<h'/?(Z')1j`'l)p.(qY,r',Knw'XP[D(,6*5(fW_;(5n+B(D9_N(:&tX(S*a))vB$k(Jo>v(>+^2)([g^)VflN)@+'#*l*qf)5@=m)"
"B7B>*$h)-*d235*[:B>*q^vG*Wv#c*i62<+efC,+8cl3+(FD<+=u_G+uFNT+XExX+Z_p^+e(%d+hp;h+um9n+Lxsx+A3?,,9Ss5,wv%A,N$4M,[rds,meEa,PaBj,h4<9-ObL,-csjA-"
")#NL-N^'R-uibY-T#-g-dfv4.oKp#.RTT+.pd(S.h.QG.f(Wl.o>D[.#//e..qL4/rmj&/;fDL/e>V</6]2L/Ys(W/L$9^/M48.0'W7t/RMgF0/#####YlS.oC+.#2M#<-MeR+%h)'##"
"&/YaE0]Wh#WtLrZ[dM_&dQd(N/(1B#SdrIh01L^#iq<on1:h#$-MWY52C-?$KTfuG3LHZ$##QiT:$Ew$G%w@b'5>>#]F[0>M3o-$.(1B#L[+Vd<UL^#aYkCj1:h#$]xR.q2C-?$@fEPA"
"3LHZ$i=w1K><jw$:n>YY.JCYG?mv1BujNe$#t7;-T<XoIu5Ad<FKWf:D49?-6J,AF8k)F.D/Wq)&APcDU^UJDk$cP9GWMDF;F5F%uuo+D#ql>-]tn92gdMk++I2GDYo'?H#-QY5+v4L#"
"[Z'x0_6qw0>j^uG9T9X1lrIk4+-E/#:,BP8&dCEHp4`PB;^5o1a:XG<2/_oDCt7FHbmndFgkqdF-4%F-)G@@-G3FGH,C%12AMM=-H1F7/Y1Vq1CTnrL'Ch*#D):@/5d:J:<N7rL9et3N"
"5Y>W-xrv9)iaV-#Cv#O-iBGO-aqdT-$GqS-'?Z-.;9^kLJ*xU.,3h'#WG5s-^2TkLcU$lLp6mp3Ai0JF7DmlE>FK@-1CUO1?6[L28V7ZQpRC`Nn4$##+0A'.6sarLNf=rL]3oiLPVZ-N"
">2oiL?Z/eG38vLF%fCkL-:Mt-0aErLc_4rL0)Uk.,gpKF,r0o-T?*1#u<*1#rENvPWpT(MT)rR#(AcY#,VC;$0o$s$41[S%8I<5&tU^l8Ym@M9^/x.:$sul&@$TM'D<5/(HTlf(LmLG)"
"P/.)*TGe`*X`EA+]x&#,a:^Y,eR>;-ikur-m-VS.qE75/u^nl/#wNM0'90/1+Qgf1/jGG23,))37D``3;]@A4?uwx4C7XY5GO9;6Khpr6O*QS7SB258WZil8[sIM9`5+/:dMbf:hfBG;"
"l($)<p@Z`<tX;A=xqrx=&4SY>*L4;?.ekr?2'LS@6?-5A:WdlA>pDMBB2&/CFJ]fCJc=GDN%u(ER=U`EVU6AFZnmxF_0NYGcH/;HgafrHk#GSIo;(5JsS_lJwl?MK%/w.L)GWfL-`8GM"
"1xo(N5:P`N9R1AO=khxOA-IYPEE*;QI^arQMvASRQ8#5SUPYlSYi:MT^+r.UbCRfUf[3GVjtj(Wn6K`WrN,AXvgcxXot*GV&6`uY*N@VZ.gw7[2)Xo[6A9P]:Yp1^>rPi^B42J_FLi+`"
"JeIc`N'+DaR?b%bVWB]bZp#>c_2ZuccJ;Vdgcr7ek%Soeo=4PfsUk1gwnKig%1-Jh)Id+i-bDciu&FS()Foc2cblh25Lx_#f/mc2k0.+49eF`#j;mc2sTEC5eh#e#?inc2t/-4Ci*He#"
"D(4)3dhuh26Ox_#h82)3l67+4:hF`#lD2)3tZNC5fk#e#Ar3)3u564Cj-He#F1OD3en(i27Rx_#jAMD3m<@+4;kF`#nMMD3uaWC5gn#e#C%OD3v;?4Ck0He#H:k`3ft1i28Ux_#lJi`3"
"nBI+4<nF`#pVi`3vgaC5hq#e#E.k`3wAH4Cl3He#JC0&4g$;i29Xx_#nS.&4oHR+4=qF`#r`.&4wmjC5it#e#G70&4xGQ4Cm6He#LLKA4h*Di2:[x_#p]IA4pN[+4>tF`#tiIA4xssC5"
"jw#e#I@KA4#NZ4Cn9He#NUg]4i0Mi2;_x_#rfe]4qTe+4?wF`#vre]4#$'D5k$$e#KIg]4$Td4Co<He#P_,#5j6Vi2<bx_#to*#5rZn+4@$G`#x%+#5$*0D5l'$e#MR,#5%Zm4Cp?He#"
"RhG>5k<`i2=ex_#vxE>5saw+4A'G`#$/F>5%09D5m*$e#O[G>5&av4CqBHe#&'32B=.wm2f2$`#q712BER804jJH`#uC12BMwOH5?N%e#Jq22BNQ79CDmRe#p7LMB@=E33h;-`#tCLMB"
"Hb]K4lSQ`#xOLMBx;D<BAW.e#M'NMBQa[TCEpRe#r@hiBACN33i>-`#vLhiBIhfK4mVQ`#$YhiB#BM<BBZ.e#O0jiBRgeTCFsRe#tI-/CBIW33jA-`#xU-/CJnoK4nYQ`#&c-/C$HV<B"
"C^.e#Q9//CSmnTC$),##$)>>#hda1#mPh5#vTg5#pHf^?ht^6_#2S4;2ZR%@&g6c+aq-vA4rkL>S=*p.F.s##GM.tB:oHY-N]IV?:K=?.Ao.R<ZjfsAD0-Q#+^+pAj9Hm#IVoE-@/c[["
"ZTC>]DlGQ8d*RH=q1a;.B'hI?\?C-i<8F4g7ioQV[b+MT..lbD-Lx'<.m7T)0KNI@'F]+Z2.HZV[#9U<.WKXD-1*6EO`ZwG&Y_bpLx;4/MAbl,M[kQ1MW6ErAQ.r`?IO,Q#$1_A.'$VC?"
"i6oS1#Yp?-=$)N$hpjHQ<E[g<T=e8.?Cn8.$<*+09MglA13P/11>;E/_UOj$lM5/Mis]Z0'f:Z[$tOZ[IVxiLkEBB-0_3uL8;5Z2)[S4=Gs2f+3QUD?=5Ok+1KtC?Nx%a+t9_eH7TDj$"
"Jk2Q8XO`Y#_0>?84I1gLq.h88mTg5#EmM^[I@'_[0c._J*'#N04El/18x7F/8o99_b`qpL7]2..KU=5Ma1tv8%N+X%jv>Q&C8H<8ng,ND,K#01`SY<-o*t^-TM@tLL_K?pewI59RNSE-"
"7lS?-H-^V[uL+Z2@vv>-qZXv7rR)W[%)^5Bp&aE-$44^-Z,g34B3lV7G),^O[^>01hM8;]+o>290h';.>X&[-:jL01tQ5293Y)w8;pU01Z,[?-C3aQ8oaT`<*#DT.=<Z][6uY<-%sY<-"
"7.;t-=*.hLR_vNB0G/?-C[SiB61BQ/0HesAN)NE-Gxf884cpA(Gs'D?,IAw8_,tD'QN1W1]^W0;l6Ne=Nr.a+SX`=-d[Y=6i)q5##a0D?6avQ8#rD?8h%wZ?sF:Z[>cMv[,>^;-0`uS."
"d>N:.fIY`1)d7Q#xiD^[oV(HOtxa*I_P_;.oYs_&XZ*.-Wdi^#iB;a'J8voL,v&nLXJ4tAm`/aYx*RV[A)eU%Z:)##N`DZ[nih5#lLM>#`0>?8@8.11,L`Y#qmQ][c<9B-GbE9.C[<9."
"wh;?8Ou*[K08o0(2xL$#=;B%BnYAj07v7b+'#vW[h6mh/?BtW[m26Q#<)d,*1=(,M;PM11c.5F%<b/t[*lu8.uVMj$@M%E+5UL,M?ir11g:5F%@n/t[.lu8.'&f,&F(O^,cjrD'T>gI-"
"nWeI-mWeI-'-@:MC8xiLVQ#<-)-@:MED4jLVQ#<-+-@:MGPFjLVQ#<---@:MI]XjLVQ#<-/-@:MT'G311^-Q#6N3a0KJco.LeiS8PF%U.>=*p.Cb_&4Ht$[-Xt^31/5bW[U?tW[g5Y>-"
":E%HF:/^5B)/HDO9[4h-3bqY?p=Vt7/ChB#@h0R0pXQI2Ok5R*sIZj)NQx:.)I?)4J;gJ2@_lA#lL$^(xmoi0WLq-MtS%)*[;w0#ENh>.=`EC#O5`P-Qmm*MmfC12x@HP8EktW[r89I*"
"qI(W3lYnV[M0'qLCKV41BG$)*bf-@->u68.==*p.t2Gx9h:_5B+ktpLK&-Q#o?;W3NYWI),p,*4lS*)**SEjLLYDm0KYmp0em-=.*$GGOG[[D4bhb>-nQ=v-%=ufL]hev6ONjj<7C4g7"
"-#fm#,7%a+W+$$$qi-<8uG@g%>^3uLVPq%/HsD^[*8WW/>+KE-vLg?-e0WE-$$esA_$d2$'#6k<+vlW--?D_&]A[][djPS3e,K*R&RW5/&;7-5Eai?B9KJ9io/vERAcM>CQ@#B#sg([K"
">0]3FwNEp.DxvuP$.0oLGD7p._FOe$E50HbJ3jU1<o?p4lS*)*4(RV[=9q&$X/o>6rwZ55<smV[i^NmLumY?.s28b-A)8R*ma?R*X#<s.t+SW[S)+p1o+;Z->R/N-AqYhL,?(&&GWEjL"
"nkHq9ohN)Y-Z9T(_eMs7]:^;.*dKK-0:%I-S;m)/u7%N-]_Bil[TXp.#Hu>-*D,XCBWkC?]<3p1kbMG)iEsM62Qsc<xOnY6J=rh55CXv6aQHo0$s%=7bWQo0*fR>6=Yf88F&eG.@XmP8"
"Z(-kLE]o;8+dUv@[V[,&cHV].&,*=.V=SF/JsIg%&w+<8LV'HOK0Y<8hbxV[GQF9B&0:?6fDGY-?)1pprsqY?_?u88aQHo05nw;-a2gr-Skt)#U7[uNRdamL9dWR8O8S9]:?hc))U&X["
"]?tW[>3:o/G*q5#Q`0U[tgt;.&PCs3j-C2:ebE-*/D+j17uqF.IiF<%$BlW[AS2Z[uBtW[L0$6#D@Ib%o_Ib%%0Pb%r]L9]/:)=-*2#HM%HgnLK1(588pWG<$]bA#u&sc<U)OX(uqIb%"
"+TSD=6:P&#'Lx>-wS,<-%V,<-CIrIM,sPoL6:)=-7u@Y-_1(F.%.Jb%%0Pb%8)4O+2>hY?CHNX(:/4O+`OVmLDA2pL%+p+M*G;pLH:)=-:2#HM5SMpLHl+87HUaSA$]bA#/c%pAU)OX("
"/LJb%>LxPB#%]u7k'xlB/HZV[2KL@-5a5<-e(MT.f$h5#UT=Z-JZa$'4DmW[1E(@-IMGHM^L4RMa0nlLQR=RMR>_<.N(wP5b,RGEb6L[0=-#(&_>3)Fd(nY6lxjDFndXfNanX7MS&c?-"
"Aa5<-x9]>-P5g,Mg&5;1;k(Z#tco;6Ij@g%a:nS.Hi/Q#@oi'#Ebl:d+d7Q#hw]][rwB`$nLs5#rlq5#/YHF%4s1G%VR)##mINX(=3<%$=cDg.VndC?J7VI?+FAw86$/@':bc/Li+kS7"
";8JoLHF_l8.V&,2a>]N0c3X(NmpL(]jh8*#1J>lLR[+##";
return openIconic_compressed_data_base85;
}
// clang-format on
void ImGuiH::showDemoIcons()
{
// clang-format off
static const char* text_icon_[] = {
"account_login", "account_logout", "action_redo", "action_undo", "align_center",
"align_left","align_right","aperture","arrow_bottom","arrow_circle_bottom","arrow_circle_left",
"arrow_circle_right","arrow_circle_top","arrow_left","arrow_right","arrow_thick_bottom","arrow_thick_left",
"arrow_thick_right","arrow_thick_top","arrow_top","audio","audio_spectrum","badge","ban","bar_chart","basket",
"battery_empty","battery_full","beaker","bell","bluetooth","bold","bolt","book","bookmark","box","briefcase",
"british_pound","browser","brush","bug","bullhorn","calculator","calendar","camera_slr","caret_bottom",
"caret_left","caret_right","caret_top","cart","chat","check","chevron_bottom","chevron_left","chevron_right",
"chevron_top","circle_check","circle_x","clipboard","clock","cloud","cloud_download","cloud_upload","cloudy",
"code","cog","collapse_down","collapse_left","collapse_right","collapse_up","command","comment_square",
"compass","contrast","copywriting","credit_card","crop","dashboard","data_transfer_download","data_transfer_upload",
"delete","dial","document","dollar","double_quote_sans_left","double_quote_sans_right","double_quote_serif_left",
"double_quote_serif_right","droplet","eject","elevator","ellipses","envelope_closed","envelope_open","euro",
"excerpt", "expend_down", "expend_left", "expend_right", "expend_up", "external_link", "eye", "eyedropper", "file", "fire", "flag", "flash",
"folder", "fork", "fullscreen_enter", "fullscreen_exit", "globe", "graph", "grid_four_up", "grid_three_up", "grid_two_up", "hard_drive", "header",
"headphones", "heart", "home", "image", "inbox", "infinity", "info", "italic", "justify_center", "justify_left", "justify_right",
"key", "laptop", "layers", "lightbulb", "link_broken", "link_intact", "list", "list_rich", "location", "lock_locked", "lock_unlocked", "loop_circular",
"loop_square", "loop", "magnifying_glass",
"map", "map_marquer", "media_pause", "media_play", "media_record", "media_skip_backward", "media_skip_forward", "media_step_backward", "media_step_forward",
"media_stop", "medical_cross", "menu", "microphone", "minus", "monitor", "moon", "move", "musical_note", "paperclip",
"pencil", "people", "person", "phone", "pie_chart", "pin", "play_circle", "plus", "power_standby", "print", "project", "pulse", "puzzle_piece",
"question_mark", "rain", "random", "reload", "resize_both", "resize_height",
"resize_width", "rss", "rss_alt", "script", "share", "share_boxed", "shield", "signal", "signpost", "sort_ascending", "sort_descending", "spreadsheet",
"star", "sun", "tablet", "tag", "tags", "target", "task", "terminal",
"text", "thumb_down", "thumb_up", "timer", "transfer", "trash", "underline", "vertical_align_bottom", "vertical_align_center", "vertical_align_top", "video",
"volume_high", "volume_low", "volume_off", "warning", "wifi", "wrench", "x", "yen", "zoom_in", "zoom_out"
};
// clang-format on
ImGui::SetNextWindowSize(ImVec2(700, 500), ImGuiCond_FirstUseEver);
if(!ImGui::Begin("Icons"))
{
ImGui::End();
return;
}
// From 0xE000 to 0xE0DF
for(int i = 0; i < 223; i++)
{
std::string utf8String;
int codePoint = i + 0xE000;
utf8String += static_cast<char>(0xE0 | (codePoint >> 12));
utf8String += static_cast<char>(0x80 | ((codePoint >> 6) & 0x3F));
utf8String += static_cast<char>(0x80 | (codePoint & 0x3F));
ImGui::PushFont(ImGuiH::getIconicFont());
ImGui::Text("%s", utf8String.c_str()); // Show icon
if(((i + 1) % 20) != 0)
ImGui::SameLine();
ImGui::PopFont();
ImGui::SetItemTooltip("%s", text_icon_[i]);
}
ImGui::End();
}

View file

@ -0,0 +1,258 @@
/*
* Copyright (c) 2023, 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) 2023, NVIDIA CORPORATION. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
/// @DOC_SKIP
#pragma once
#include <imgui.h>
namespace ImGuiH {
void addIconicFont(float fontSize = 14.F); // Call this once in the application after ImGui is initialized
void showDemoIcons(); // Show all icons in a separated window
ImFont* getIconicFont(); // Return the iconic font
// Ex: ImGui::PushFont(ImGuiH::getIconicFont());
// ImGui::Button(ImGuiH::icon_account_login);
// ImGui::PopFont();
[[maybe_unused]] static const char* icon_account_login = (char*)u8"\ue000";
[[maybe_unused]] static const char* icon_account_logout = (char*)u8"\ue001";
[[maybe_unused]] static const char* icon_action_redo = (char*)u8"\ue002";
[[maybe_unused]] static const char* icon_action_undo = (char*)u8"\ue003";
[[maybe_unused]] static const char* icon_align_center = (char*)u8"\ue004";
[[maybe_unused]] static const char* icon_align_left = (char*)u8"\ue005";
[[maybe_unused]] static const char* icon_align_right = (char*)u8"\ue006";
[[maybe_unused]] static const char* icon_aperture = (char*)u8"\ue007";
[[maybe_unused]] static const char* icon_arrow_bottom = (char*)u8"\ue008";
[[maybe_unused]] static const char* icon_arrow_circle_bottom = (char*)u8"\ue009";
[[maybe_unused]] static const char* icon_arrow_circle_left = (char*)u8"\ue00A";
[[maybe_unused]] static const char* icon_arrow_circle_right = (char*)u8"\ue00B";
[[maybe_unused]] static const char* icon_arrow_circle_top = (char*)u8"\ue00C";
[[maybe_unused]] static const char* icon_arrow_left = (char*)u8"\ue00D";
[[maybe_unused]] static const char* icon_arrow_right = (char*)u8"\ue00E";
[[maybe_unused]] static const char* icon_arrow_thick_bottom = (char*)u8"\ue00F";
[[maybe_unused]] static const char* icon_arrow_thick_left = (char*)u8"\ue010";
[[maybe_unused]] static const char* icon_arrow_thick_right = (char*)u8"\ue011";
[[maybe_unused]] static const char* icon_arrow_thick_top = (char*)u8"\ue012";
[[maybe_unused]] static const char* icon_arrow_top = (char*)u8"\ue013";
[[maybe_unused]] static const char* icon_audio = (char*)u8"\ue014";
[[maybe_unused]] static const char* icon_audio_spectrum = (char*)u8"\ue015";
[[maybe_unused]] static const char* icon_badge = (char*)u8"\ue016";
[[maybe_unused]] static const char* icon_ban = (char*)u8"\ue017";
[[maybe_unused]] static const char* icon_bar_chart = (char*)u8"\ue018";
[[maybe_unused]] static const char* icon_basket = (char*)u8"\ue019";
[[maybe_unused]] static const char* icon_battery_empty = (char*)u8"\ue01A";
[[maybe_unused]] static const char* icon_battery_full = (char*)u8"\ue01B";
[[maybe_unused]] static const char* icon_beaker = (char*)u8"\ue01C";
[[maybe_unused]] static const char* icon_bell = (char*)u8"\ue01D";
[[maybe_unused]] static const char* icon_bluetooth = (char*)u8"\ue01E";
[[maybe_unused]] static const char* icon_bold = (char*)u8"\ue01F";
[[maybe_unused]] static const char* icon_bolt = (char*)u8"\ue020";
[[maybe_unused]] static const char* icon_book = (char*)u8"\ue021";
[[maybe_unused]] static const char* icon_bookmark = (char*)u8"\ue022";
[[maybe_unused]] static const char* icon_box = (char*)u8"\ue023";
[[maybe_unused]] static const char* icon_briefcase = (char*)u8"\ue024";
[[maybe_unused]] static const char* icon_british_pound = (char*)u8"\ue025";
[[maybe_unused]] static const char* icon_browser = (char*)u8"\ue026";
[[maybe_unused]] static const char* icon_brush = (char*)u8"\ue027";
[[maybe_unused]] static const char* icon_bug = (char*)u8"\ue028";
[[maybe_unused]] static const char* icon_bullhorn = (char*)u8"\ue029";
[[maybe_unused]] static const char* icon_calculator = (char*)u8"\ue02A";
[[maybe_unused]] static const char* icon_calendar = (char*)u8"\ue02B";
[[maybe_unused]] static const char* icon_camera_slr = (char*)u8"\ue02C";
[[maybe_unused]] static const char* icon_caret_bottom = (char*)u8"\ue02D";
[[maybe_unused]] static const char* icon_caret_left = (char*)u8"\ue02E";
[[maybe_unused]] static const char* icon_caret_right = (char*)u8"\ue02F";
[[maybe_unused]] static const char* icon_caret_top = (char*)u8"\ue030";
[[maybe_unused]] static const char* icon_cart = (char*)u8"\ue031";
[[maybe_unused]] static const char* icon_chat = (char*)u8"\ue032";
[[maybe_unused]] static const char* icon_check = (char*)u8"\ue033";
[[maybe_unused]] static const char* icon_chevron_bottom = (char*)u8"\ue034";
[[maybe_unused]] static const char* icon_chevron_left = (char*)u8"\ue035";
[[maybe_unused]] static const char* icon_chevron_right = (char*)u8"\ue036";
[[maybe_unused]] static const char* icon_chevron_top = (char*)u8"\ue037";
[[maybe_unused]] static const char* icon_circle_check = (char*)u8"\ue038";
[[maybe_unused]] static const char* icon_circle_x = (char*)u8"\ue039";
[[maybe_unused]] static const char* icon_clipboard = (char*)u8"\ue03A";
[[maybe_unused]] static const char* icon_clock = (char*)u8"\ue03B";
[[maybe_unused]] static const char* icon_cloud_download = (char*)u8"\ue03C";
[[maybe_unused]] static const char* icon_cloud_upload = (char*)u8"\ue03D";
[[maybe_unused]] static const char* icon_cloud = (char*)u8"\ue03E";
[[maybe_unused]] static const char* icon_cloudy = (char*)u8"\ue03F";
[[maybe_unused]] static const char* icon_code = (char*)u8"\ue040";
[[maybe_unused]] static const char* icon_cog = (char*)u8"\ue041";
[[maybe_unused]] static const char* icon_collapse_down = (char*)u8"\ue042";
[[maybe_unused]] static const char* icon_collapse_left = (char*)u8"\ue043";
[[maybe_unused]] static const char* icon_collapse_right = (char*)u8"\ue044";
[[maybe_unused]] static const char* icon_collapse_up = (char*)u8"\ue045";
[[maybe_unused]] static const char* icon_command = (char*)u8"\ue046";
[[maybe_unused]] static const char* icon_comment_square = (char*)u8"\ue047";
[[maybe_unused]] static const char* icon_compass = (char*)u8"\ue048";
[[maybe_unused]] static const char* icon_contrast = (char*)u8"\ue049";
[[maybe_unused]] static const char* icon_copywriting = (char*)u8"\ue04A";
[[maybe_unused]] static const char* icon_credit_card = (char*)u8"\ue04B";
[[maybe_unused]] static const char* icon_crop = (char*)u8"\ue04C";
[[maybe_unused]] static const char* icon_dashboard = (char*)u8"\ue04D";
[[maybe_unused]] static const char* icon_data_transfer_download = (char*)u8"\ue04E";
[[maybe_unused]] static const char* icon_data_transfer_upload = (char*)u8"\ue04F";
[[maybe_unused]] static const char* icon_delete = (char*)u8"\ue050";
[[maybe_unused]] static const char* icon_dial = (char*)u8"\ue051";
[[maybe_unused]] static const char* icon_document = (char*)u8"\ue052";
[[maybe_unused]] static const char* icon_dollar = (char*)u8"\ue053";
[[maybe_unused]] static const char* icon_double_quote_sans_left = (char*)u8"\ue054";
[[maybe_unused]] static const char* icon_double_quote_sans_right = (char*)u8"\ue055";
[[maybe_unused]] static const char* icon_double_quote_serif_left = (char*)u8"\ue056";
[[maybe_unused]] static const char* icon_double_quote_serif_right = (char*)u8"\ue057";
[[maybe_unused]] static const char* icon_droplet = (char*)u8"\ue058";
[[maybe_unused]] static const char* icon_eject = (char*)u8"\ue059";
[[maybe_unused]] static const char* icon_elevator = (char*)u8"\ue05A";
[[maybe_unused]] static const char* icon_ellipses = (char*)u8"\ue05B";
[[maybe_unused]] static const char* icon_envelope_closed = (char*)u8"\ue05C";
[[maybe_unused]] static const char* icon_envelope_open = (char*)u8"\ue05D";
[[maybe_unused]] static const char* icon_euro = (char*)u8"\ue05E";
[[maybe_unused]] static const char* icon_excerpt = (char*)u8"\ue05F";
[[maybe_unused]] static const char* icon_expend_down = (char*)u8"\ue060";
[[maybe_unused]] static const char* icon_expend_left = (char*)u8"\ue061";
[[maybe_unused]] static const char* icon_expend_right = (char*)u8"\ue062";
[[maybe_unused]] static const char* icon_expend_up = (char*)u8"\ue063";
[[maybe_unused]] static const char* icon_external_link = (char*)u8"\ue064";
[[maybe_unused]] static const char* icon_eye = (char*)u8"\ue065";
[[maybe_unused]] static const char* icon_eyedropper = (char*)u8"\ue066";
[[maybe_unused]] static const char* icon_file = (char*)u8"\ue067";
[[maybe_unused]] static const char* icon_fire = (char*)u8"\ue068";
[[maybe_unused]] static const char* icon_flag = (char*)u8"\ue069";
[[maybe_unused]] static const char* icon_flash = (char*)u8"\ue06A";
[[maybe_unused]] static const char* icon_folder = (char*)u8"\ue06B";
[[maybe_unused]] static const char* icon_fork = (char*)u8"\ue06C";
[[maybe_unused]] static const char* icon_fullscreen_enter = (char*)u8"\ue06D";
[[maybe_unused]] static const char* icon_fullscreen_exit = (char*)u8"\ue06E";
[[maybe_unused]] static const char* icon_globe = (char*)u8"\ue06F";
[[maybe_unused]] static const char* icon_graph = (char*)u8"\ue070";
[[maybe_unused]] static const char* icon_grid_four_up = (char*)u8"\ue071";
[[maybe_unused]] static const char* icon_grid_three_up = (char*)u8"\ue072";
[[maybe_unused]] static const char* icon_grid_two_up = (char*)u8"\ue073";
[[maybe_unused]] static const char* icon_hard_drive = (char*)u8"\ue074";
[[maybe_unused]] static const char* icon_header = (char*)u8"\ue075";
[[maybe_unused]] static const char* icon_headphones = (char*)u8"\ue076";
[[maybe_unused]] static const char* icon_heart = (char*)u8"\ue077";
[[maybe_unused]] static const char* icon_home = (char*)u8"\ue078";
[[maybe_unused]] static const char* icon_image = (char*)u8"\ue079";
[[maybe_unused]] static const char* icon_inbox = (char*)u8"\ue07A";
[[maybe_unused]] static const char* icon_infinity = (char*)u8"\ue07B";
[[maybe_unused]] static const char* icon_info = (char*)u8"\ue07C";
[[maybe_unused]] static const char* icon_italic = (char*)u8"\ue07D";
[[maybe_unused]] static const char* icon_justify_center = (char*)u8"\ue07E";
[[maybe_unused]] static const char* icon_justify_left = (char*)u8"\ue07F";
[[maybe_unused]] static const char* icon_justify_right = (char*)u8"\ue080";
[[maybe_unused]] static const char* icon_key = (char*)u8"\ue081";
[[maybe_unused]] static const char* icon_laptop = (char*)u8"\ue082";
[[maybe_unused]] static const char* icon_layers = (char*)u8"\ue083";
[[maybe_unused]] static const char* icon_lightbulb = (char*)u8"\ue084";
[[maybe_unused]] static const char* icon_link_broken = (char*)u8"\ue085";
[[maybe_unused]] static const char* icon_link_intact = (char*)u8"\ue086";
[[maybe_unused]] static const char* icon_list = (char*)u8"\ue087";
[[maybe_unused]] static const char* icon_list_rich = (char*)u8"\ue088";
[[maybe_unused]] static const char* icon_location = (char*)u8"\ue089";
[[maybe_unused]] static const char* icon_lock_locked = (char*)u8"\ue08A";
[[maybe_unused]] static const char* icon_lock_unlocked = (char*)u8"\ue08B";
[[maybe_unused]] static const char* icon_loop_circular = (char*)u8"\ue08C";
[[maybe_unused]] static const char* icon_loop_square = (char*)u8"\ue08D";
[[maybe_unused]] static const char* icon_loop = (char*)u8"\ue08E";
[[maybe_unused]] static const char* icon_magnifying_glass = (char*)u8"\ue08F";
[[maybe_unused]] static const char* icon_map = (char*)u8"\ue090";
[[maybe_unused]] static const char* icon_map_marquer = (char*)u8"\ue091";
[[maybe_unused]] static const char* icon_media_pause = (char*)u8"\ue092";
[[maybe_unused]] static const char* icon_media_play = (char*)u8"\ue093";
[[maybe_unused]] static const char* icon_media_record = (char*)u8"\ue094";
[[maybe_unused]] static const char* icon_media_skip_backward = (char*)u8"\ue095";
[[maybe_unused]] static const char* icon_media_skip_forward = (char*)u8"\ue096";
[[maybe_unused]] static const char* icon_media_step_backward = (char*)u8"\ue097";
[[maybe_unused]] static const char* icon_media_step_forward = (char*)u8"\ue098";
[[maybe_unused]] static const char* icon_media_stop = (char*)u8"\ue099";
[[maybe_unused]] static const char* icon_medical_cross = (char*)u8"\ue09A";
[[maybe_unused]] static const char* icon_menu = (char*)u8"\ue09B";
[[maybe_unused]] static const char* icon_microphone = (char*)u8"\ue09C";
[[maybe_unused]] static const char* icon_minus = (char*)u8"\ue09D";
[[maybe_unused]] static const char* icon_monitor = (char*)u8"\ue09E";
[[maybe_unused]] static const char* icon_moon = (char*)u8"\ue09F";
[[maybe_unused]] static const char* icon_move = (char*)u8"\ue0A0";
[[maybe_unused]] static const char* icon_musical_note = (char*)u8"\ue0A1";
[[maybe_unused]] static const char* icon_paperclip = (char*)u8"\ue0A2";
[[maybe_unused]] static const char* icon_pencil = (char*)u8"\ue0A3";
[[maybe_unused]] static const char* icon_people = (char*)u8"\ue0A4";
[[maybe_unused]] static const char* icon_person = (char*)u8"\ue0A5";
[[maybe_unused]] static const char* icon_phone = (char*)u8"\ue0A6";
[[maybe_unused]] static const char* icon_pie_chart = (char*)u8"\ue0A7";
[[maybe_unused]] static const char* icon_pin = (char*)u8"\ue0A8";
[[maybe_unused]] static const char* icon_play_circle = (char*)u8"\ue0A9";
[[maybe_unused]] static const char* icon_plus = (char*)u8"\ue0AA";
[[maybe_unused]] static const char* icon_power_standby = (char*)u8"\ue0AB";
[[maybe_unused]] static const char* icon_print = (char*)u8"\ue0AC";
[[maybe_unused]] static const char* icon_project = (char*)u8"\ue0AD";
[[maybe_unused]] static const char* icon_pulse = (char*)u8"\ue0AE";
[[maybe_unused]] static const char* icon_puzzle_piece = (char*)u8"\ue0AF";
[[maybe_unused]] static const char* icon_question_mark = (char*)u8"\ue0B0";
[[maybe_unused]] static const char* icon_rain = (char*)u8"\ue0B1";
[[maybe_unused]] static const char* icon_random = (char*)u8"\ue0B2";
[[maybe_unused]] static const char* icon_reload = (char*)u8"\ue0B3";
[[maybe_unused]] static const char* icon_resize_both = (char*)u8"\ue0B4";
[[maybe_unused]] static const char* icon_resize_height = (char*)u8"\ue0B5";
[[maybe_unused]] static const char* icon_resize_width = (char*)u8"\ue0B6";
[[maybe_unused]] static const char* icon_rss = (char*)u8"\ue0B7";
[[maybe_unused]] static const char* icon_rss_alt = (char*)u8"\ue0B8";
[[maybe_unused]] static const char* icon_script = (char*)u8"\ue0B9";
[[maybe_unused]] static const char* icon_share = (char*)u8"\ue0BA";
[[maybe_unused]] static const char* icon_share_boxed = (char*)u8"\ue0BB";
[[maybe_unused]] static const char* icon_shield = (char*)u8"\ue0BC";
[[maybe_unused]] static const char* icon_signal = (char*)u8"\ue0BD";
[[maybe_unused]] static const char* icon_signpost = (char*)u8"\ue0BE";
[[maybe_unused]] static const char* icon_sort_ascending = (char*)u8"\ue0BF";
[[maybe_unused]] static const char* icon_sort_descending = (char*)u8"\ue0C0";
[[maybe_unused]] static const char* icon_spreadsheet = (char*)u8"\ue0C1";
[[maybe_unused]] static const char* icon_star = (char*)u8"\ue0C2";
[[maybe_unused]] static const char* icon_sun = (char*)u8"\ue0C3";
[[maybe_unused]] static const char* icon_tablet = (char*)u8"\ue0C4";
[[maybe_unused]] static const char* icon_tag = (char*)u8"\ue0C5";
[[maybe_unused]] static const char* icon_tags = (char*)u8"\ue0C6";
[[maybe_unused]] static const char* icon_target = (char*)u8"\ue0C7";
[[maybe_unused]] static const char* icon_task = (char*)u8"\ue0C8";
[[maybe_unused]] static const char* icon_terminal = (char*)u8"\ue0C9";
[[maybe_unused]] static const char* icon_text = (char*)u8"\ue0CA";
[[maybe_unused]] static const char* icon_thumb_down = (char*)u8"\ue0CB";
[[maybe_unused]] static const char* icon_thumb_up = (char*)u8"\ue0CC";
[[maybe_unused]] static const char* icon_timer = (char*)u8"\ue0CD";
[[maybe_unused]] static const char* icon_transfer = (char*)u8"\ue0CE";
[[maybe_unused]] static const char* icon_trash = (char*)u8"\ue0CF";
[[maybe_unused]] static const char* icon_underline = (char*)u8"\ue0D0";
[[maybe_unused]] static const char* icon_vertical_align_bottom = (char*)u8"\ue0D1";
[[maybe_unused]] static const char* icon_vertical_align_center = (char*)u8"\ue0D2";
[[maybe_unused]] static const char* icon_vertical_align_top = (char*)u8"\ue0D3";
[[maybe_unused]] static const char* icon_video = (char*)u8"\ue0D4";
[[maybe_unused]] static const char* icon_volume_high = (char*)u8"\ue0D5";
[[maybe_unused]] static const char* icon_volume_low = (char*)u8"\ue0D6";
[[maybe_unused]] static const char* icon_volume_off = (char*)u8"\ue0D7";
[[maybe_unused]] static const char* icon_warning = (char*)u8"\ue0D8";
[[maybe_unused]] static const char* icon_wifi = (char*)u8"\ue0D9";
[[maybe_unused]] static const char* icon_wrench = (char*)u8"\ue0DA";
[[maybe_unused]] static const char* icon_x = (char*)u8"\ue0DB";
[[maybe_unused]] static const char* icon_yen = (char*)u8"\ue0DC";
[[maybe_unused]] static const char* icon_zoom_in = (char*)u8"\ue0DD";
[[maybe_unused]] static const char* icon_zoom_out = (char*)u8"\ue0DE";
} // namespace ImGuiH

View file

@ -0,0 +1,806 @@
/*
* Copyright (c) 2019-2023, 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) 2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include "imgui.h"
#include "imgui/imgui_orient.h"
#include "imgui_internal.h" // ImSaturate
#include <algorithm>
#include <cassert>
ImVector<ImVec3> ImOrient::s_SphTri;
ImVector<ImU32> ImOrient::s_SphCol;
ImVector<ImVec2> ImOrient::s_SphTriProj;
ImVector<ImU32> ImOrient::s_SphColLight;
ImVector<ImVec3> ImOrient::s_ArrowTri[4];
ImVector<ImVec2> ImOrient::s_ArrowTriProj[4];
ImVector<ImVec3> ImOrient::s_ArrowNorm[4];
ImVector<ImU32> ImOrient::s_ArrowColLight[4];
namespace ImGui {
IMGUI_API bool QuaternionGizmo(const char* label, ImQuat& quat)
{
ImOrient orient;
orient.Qt = quat;
orient.Axis = ImVec3(1.0f, 0.0f, 0.0f);
orient.Angle = 0;
orient.Dir.x = orient.Dir.y = orient.Dir.z = 0;
orient.m_AAMode = false; // Axis & angle mode hidden
orient.m_IsDir = false;
orient.m_ShowDir = ImVec3(0.0f, 0.0f, 0.0f);
orient.m_DirColor = 0xff00ffff;
orient.ConvertToAxisAngle();
bool ret = orient.Draw(label);
if(ret)
{
quat = orient.Qt;
}
return ret;
}
IMGUI_API bool AxisAngleGizmo(const char* label, ImVec3& axis, float& angle)
{
ImOrient orient;
orient.Qt = ImQuat();
orient.Axis = axis;
orient.Angle = angle;
orient.Dir = ImVec3(0.0f, 0.0f, 0.0f);
orient.m_AAMode = true; // Axis & angle mode hidden
orient.m_IsDir = true;
orient.m_ShowDir = ImVec3(0.0f, 0.0f, 0.0f);
orient.m_DirColor = 0xff00ffff;
orient.ConvertFromAxisAngle();
bool ret = orient.Draw(label);
if(ret)
{
orient.ConvertToAxisAngle();
axis = orient.Axis;
angle = orient.Angle;
}
return ret;
}
IMGUI_API bool DirectionGizmo(const char* label, float* dir, bool flip /*= false*/, bool show_info /*= false*/)
{
ImOrient orient;
orient.Qt = ImQuat();
if(flip)
orient.Dir = {-dir[0], -dir[1], -dir[2]};
else
orient.Dir = {dir[0], dir[1], dir[2]};
orient.Axis = ImVec3(1.0f, 0.0f, 0.0f);
orient.Angle = 0.0f;
orient.m_AAMode = false; // Axis & angle mode hidden
orient.m_IsDir = true;
orient.m_ShowDir = ImVec3(1.0f, 0.0f, 0.0f);
orient.m_DirColor = 0xffff0000;
orient.QuatFromDir(orient.Qt, orient.Dir);
orient.ConvertToAxisAngle();
bool ret = orient.Draw(label, show_info);
if(ret)
{
ImVec3 d = orient.Qt.Rotate(ImVec3(1, 0, 0));
d = d.Div(d.Length());
if(flip)
orient.Dir = {-d.x, -d.y, -d.z};
else
orient.Dir = d;
dir[0] = orient.Dir.x;
dir[1] = orient.Dir.y;
dir[2] = orient.Dir.z;
}
return ret;
}
} // namespace ImGui
IMGUI_API bool ImOrient::Draw(const char* label, bool show_info /*= false*/)
{
// ImGuiIO& io = ImGui::GetIO();
ImGuiStyle& style = ImGui::GetStyle();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
if(ImOrient::s_SphTri.empty())
{
ImOrient::CreateArrow();
ImOrient::CreateSphere();
}
ImGui::PushID(label);
ImGui::BeginGroup();
bool value_changed = false;
ImGui::Text("%s", label);
if(show_info)
{
// Summary
if(m_AAMode)
{
ImGui::Text("Axis={%.2f,%.2f,%.2f} Angle=%.0f%c", Axis.x, Axis.y, Axis.z, Angle, 176);
}
else if(m_IsDir)
{
ImGui::Text("Dir={%.2f,%.2f,%.2f}", Dir.x, Dir.y, Dir.z);
}
else
{
ImGui::Text("Quat={x:%.2f,y:%.2f,z:%.2f,s:%.2f}", Qt.x, Qt.y, Qt.z, Qt.w);
}
}
ImVec2 orient_pos = ImGui::GetCursorScreenPos();
float sv_orient_size = std::min(ImGui::CalcItemWidth(), float(GIZMO_SIZE));
float w = sv_orient_size;
float h = sv_orient_size;
// We want to generate quaternion rotations relative to the quaternion in the 'down' press state.
// This gives us cleaner control over rotation (it feels better/more natural)
static ImQuat origQuat;
static ImVec3 coordOld;
bool highlighted = false;
ImGui::InvisibleButton("orient", ImVec2(sv_orient_size, sv_orient_size));
if(ImGui::IsItemActive())
{
highlighted = true;
ImVec2 mouse = ImGui::GetMousePos() - orient_pos;
if(ImGui::IsMouseClicked(0))
{
origQuat = Qt;
coordOld = ImVec3(QuatIX((int)mouse.x, w, h), QuatIY((int)mouse.y, w, h), 1.0f);
}
else if(ImGui::IsMouseDragging(0))
{
//ImGui::ResetMouseDragDelta(0);
ImVec3 coord(QuatIX((int)mouse.x, w, h), QuatIY((int)mouse.y, w, h), 1.0f);
ImVec3 pVec = AxisTransform.Transform(coord);
ImVec3 oVec = AxisTransform.Transform(coordOld);
coord.z = 0.0f;
float n0 = oVec.Length();
float n1 = pVec.Length();
if(n0 > FLT_EPSILON && n1 > FLT_EPSILON)
{
ImVec3 v0 = oVec.Div(n0);
ImVec3 v1 = pVec.Div(n1);
ImVec3 axis = v0.Cross(v1);
float sa = axis.Length();
float ca = v0.Dot(v1);
float angle = (float)atan2(sa, ca);
if(coord.x * coord.x + coord.y * coord.y > 1.0f)
angle *= 1.0f + 1.5f * (coord.Length() - 1.0f);
ImQuat qrot, qres, qorig;
QuatFromAxisAngle(qrot, axis, angle);
float nqorig =
sqrtf(origQuat.x * origQuat.x + origQuat.y * origQuat.y + origQuat.z * origQuat.z + origQuat.w * origQuat.w);
if(fabsf(nqorig) > FLT_EPSILON * FLT_EPSILON)
{
qorig = origQuat.Div(nqorig);
qres = qrot.Mult(qorig);
Qt = qres;
}
else
{
Qt = qrot;
}
//origQuat = Qt;
value_changed = true;
}
}
draw_list->AddRectFilled(orient_pos, orient_pos + ImVec2(sv_orient_size, sv_orient_size),
ImColor(style.Colors[ImGuiCol_FrameBgActive]), style.FrameRounding);
}
else
{
highlighted = ImGui::IsItemHovered();
draw_list->AddRectFilled(orient_pos, orient_pos + ImVec2(sv_orient_size, sv_orient_size),
ImColor(highlighted ? style.Colors[ImGuiCol_FrameBgHovered] : style.Colors[ImGuiCol_FrameBg]),
style.FrameRounding);
}
float normDir = m_ShowDir.Length();
bool drawDir = m_IsDir || (normDir > FLT_EPSILON);
ImVec2 inner_pos = orient_pos;
float inner_size = w;
if(drawDir)
{
inner_size = sv_orient_size;
}
else
{
inner_pos.x += sv_orient_size * .25f * .5f;
inner_pos.y += sv_orient_size * .25f * .5f;
inner_size *= .75f;
}
ImQuat quat;
int i, j, k, l, m;
// normalize quaternion
float qn = sqrtf(Qt.w * Qt.w + Qt.x * Qt.x + Qt.y * Qt.y + Qt.z * Qt.z);
if(qn > FLT_EPSILON)
{
quat.x = (float)Qt.x / qn;
quat.y = (float)Qt.y / qn;
quat.z = (float)Qt.z / qn;
quat.w = (float)Qt.w / qn;
}
else
{
quat.x = quat.y = quat.z = 0.0f;
quat.w = 1.0f;
}
ImColor alpha(1.0f, 1.0f, 1.0f, highlighted ? 1.0f : 0.75f);
// check if frame is right-handed
ImVec3 px = AxisTransform.Transform(ImVec3(1.0f, 0.0f, 0.0f));
ImVec3 py = AxisTransform.Transform(ImVec3(0.0f, 1.0f, 0.0f));
ImVec3 pz = AxisTransform.Transform(ImVec3(0.0f, 0.0f, 1.0f));
ImVec3 ez = px.Cross(py);
// Use the handedness of the frame matrix to determine cull direction
bool frameRightHanded = pz.Dot(ez) >= 0;
float cullDir = frameRightHanded ? 1.0f : -1.0f;
// Drawing an arrow
if(drawDir)
{
ImVec3 dir = m_ShowDir;
if(normDir < FLT_EPSILON)
{
normDir = 1;
dir.x = 1;
}
ImVec3 kVec = dir;
ImVec3 rotDirAxis = {0, -kVec.z, kVec.y};
if(rotDirAxis.Dot(rotDirAxis) < FLT_EPSILON * FLT_EPSILON)
{
rotDirAxis.x = rotDirAxis.y = 0;
rotDirAxis.z = 1;
}
float rotDirAngle = (float)acos(kVec.x / normDir);
ImQuat rotDirQuat;
QuatFromAxisAngle(rotDirQuat, rotDirAxis, rotDirAngle);
kVec = ImVec3(1.0f, 0.0f, 0.0f);
kVec = rotDirQuat.Rotate(kVec);
kVec = quat.Rotate(kVec);
for(k = 0; k < 4; ++k) // 4 parts of the arrow
{
// draw order
ImVec3 arrowDir = AxisTransform.Transform(kVec);
j = (arrowDir.z > 0) ? 3 - k : k;
assert(s_ArrowTriProj[j].size() == (s_ArrowTri[j].size()) && s_ArrowColLight[j].size() == s_ArrowTri[j].size()
&& s_ArrowNorm[j].size() == s_ArrowTri[j].size());
const int ntri = (int)s_ArrowTri[j].size();
for(i = 0; i < ntri; ++i)
{
ImVec3 coord = s_ArrowTri[j][i];
ImVec3 norm = s_ArrowNorm[j][i];
if(coord.x > 0)
coord.x = 2.5f * coord.x - 2.0f;
else
coord.x += 0.2f;
coord.y *= 1.5f;
coord.z *= 1.5f;
coord = rotDirQuat.Rotate(coord);
coord = quat.Rotate(coord);
coord = AxisTransform.Transform(coord);
norm = rotDirQuat.Rotate(norm);
norm = quat.Rotate(norm);
norm = AxisTransform.Transform(norm);
s_ArrowTriProj[j][i] = ImVec2(QuatPX(coord.x, w, h), QuatPY(coord.y, w, h));
ImU32 col = (m_DirColor | 0xff000000) & alpha;
s_ArrowColLight[j][i] = ColorBlend(0xff000000, col, fabsf(ImClamp(norm.z, -1.0f, 1.0f)));
}
DrawTriangles(draw_list, inner_pos, s_ArrowTriProj[j], s_ArrowColLight[j], ntri, cullDir);
}
}
else
{
// draw arrows & sphere
const float SPH_RADIUS = 0.75f;
for(m = 0; m < 2; ++m) // m=0: back, m=1: front
{
for(l = 0; l < 3; ++l) // draw 3 arrows
{
ImVec3 kVec(1, 0, 0);
if(l == 1)
{
kVec = kVec.RotZ();
}
else if(l == 2)
{
kVec = kVec.RotY();
}
kVec = quat.Rotate(kVec);
for(k = 0; k < 4; ++k) // 4 parts of the arrow
{
// draw order
ImVec3 arrowCoord = AxisTransform.Transform(kVec);
j = (arrowCoord.z > 0) ? 3 - k : k;
bool cone = true;
if((m == 0 && arrowCoord.z > 0) || (m == 1 && arrowCoord.z <= 0))
{
if(j == ImOrient::ARROW_CONE || j == ImOrient::ARROW_CONE_CAP) // do not draw cone
continue;
else
cone = false;
}
assert(ImOrient::s_ArrowTriProj[j].size() == (ImOrient::s_ArrowTri[j].size())
&& ImOrient::s_ArrowColLight[j].size() == ImOrient::s_ArrowTri[j].size()
&& ImOrient::s_ArrowNorm[j].size() == ImOrient::s_ArrowTri[j].size());
const int ntri = (int)ImOrient::s_ArrowTri[j].size();
for(i = 0; i < ntri; ++i)
{
ImVec3 coord = s_ArrowTri[j][i];
if(cone && coord.x <= 0)
coord.x = SPH_RADIUS;
else if(!cone && coord.x > 0)
coord.x = -SPH_RADIUS;
ImVec3 norm = s_ArrowNorm[j][i];
if(l == 1)
{
coord = coord.RotZ();
norm = norm.RotZ();
}
else if(l == 2)
{
coord = coord.RotY();
norm = norm.RotY();
}
coord = quat.Rotate(coord);
coord = AxisTransform.Transform(coord);
norm = quat.Rotate(norm);
norm = AxisTransform.Transform(norm);
s_ArrowTriProj[j][i] = ImVec2(QuatPX(coord.x, inner_size, inner_size), QuatPY(coord.y, inner_size, inner_size));
float fade = (m == 0 && coord.z < 0) ? ImClamp(2.0f * coord.z * coord.z, 0.0f, 1.0f) : 0;
float alphaFade = 1.0f;
alphaFade = alpha.Value.w;
alphaFade *= (1.0f - fade);
ImColor alphaFadeCol(1.0f, 1.0f, 1.0f, alphaFade);
ImU32 col = (l == 0) ? 0xffff0000 : ((l == 1) ? 0xff00ff00 : 0xff0000ff);
s_ArrowColLight[j][i] = ColorBlend(0xff000000, col, fabsf(ImClamp(norm.z, -1.0f, 1.0f))) & ImU32(alphaFadeCol);
}
DrawTriangles(draw_list, inner_pos, s_ArrowTriProj[j], s_ArrowColLight[j], ntri, cullDir);
}
}
if(m == 0)
{
const int ntri = (int)ImOrient::s_SphTri.size();
for(i = 0; i < ntri; ++i) // draw sphere
{
ImVec3 coord = s_SphTri[i].Mult(SPH_RADIUS);
coord = quat.Rotate(coord);
coord = AxisTransform.Transform(coord);
s_SphTriProj[i] = ImVec2(QuatPX(coord.x, inner_size, inner_size), QuatPY(coord.y, inner_size, inner_size));
s_SphColLight[i] = ColorBlend(0xff000000, s_SphCol[i], fabsf(ImClamp(coord.z / SPH_RADIUS, -1.0f, 1.0f))) & ImU32(alpha);
}
DrawTriangles(draw_list, inner_pos, s_SphTriProj, s_SphColLight, ntri, cullDir);
}
}
// draw x
draw_list->AddLine(orient_pos + ImVec2(w - 12, h - 36), orient_pos + ImVec2(w - 12 + 5, h - 36 + 5), 0xff0000c0);
draw_list->AddLine(orient_pos + ImVec2(w - 12 + 5, h - 36), orient_pos + ImVec2(w - 12, h - 36 + 5), 0xff0000c0);
// draw y
draw_list->AddLine(orient_pos + ImVec2(w - 12, h - 25), orient_pos + ImVec2(w - 12 + 3, h - 25 + 4), 0xff00c000);
draw_list->AddLine(orient_pos + ImVec2(w - 12 + 5, h - 25), orient_pos + ImVec2(w - 12, h - 25 + 7), 0xff00c000);
// draw z
draw_list->AddLine(orient_pos + ImVec2(w - 12, h - 12), orient_pos + ImVec2(w - 12 + 5, h - 12), 0xffc00000);
draw_list->AddLine(orient_pos + ImVec2(w - 12, h - 12 + 5), orient_pos + ImVec2(w - 12 + 5, h - 12 + 5), 0xffc00000);
draw_list->AddLine(orient_pos + ImVec2(w - 12, h - 12 + 5), orient_pos + ImVec2(w - 12 + 5, h - 12), 0xffc00000);
}
ImGui::EndGroup();
ImGui::PopID();
return value_changed;
}
void ImOrient::DrawTriangles(ImDrawList* draw_list,
const ImVec2& offset,
const ImVector<ImVec2>& triProj,
const ImVector<ImU32>& colLight,
int numVertices,
float cullDir)
{
const ImVec2 uv = ImGui::GetFontTexUvWhitePixel();
assert(numVertices % 3 == 0);
draw_list->PrimReserve(numVertices, numVertices); // num vert/indices
for(int ii = 0; ii < numVertices / 3; ii++)
{
ImVec2 v1 = offset + triProj[ii * 3];
ImVec2 v2 = offset + triProj[ii * 3 + 1];
ImVec2 v3 = offset + triProj[ii * 3 + 2];
// 2D cross product to do culling
ImVec2 d1 = ImVec2Subtract(v2, v1);
ImVec2 d2 = ImVec2Subtract(v3, v1);
float c = ImVec2Cross(d1, d2) * cullDir;
if(c > 0.0f)
{
v2 = v1;
v3 = v1;
}
draw_list->PrimWriteIdx(ImDrawIdx(draw_list->_VtxCurrentIdx));
draw_list->PrimWriteIdx(ImDrawIdx(draw_list->_VtxCurrentIdx + 1));
draw_list->PrimWriteIdx(ImDrawIdx(draw_list->_VtxCurrentIdx + 2));
draw_list->PrimWriteVtx(v1, uv, colLight[ii * 3]);
draw_list->PrimWriteVtx(v2, uv, colLight[ii * 3 + 1]);
draw_list->PrimWriteVtx(v3, uv, colLight[ii * 3 + 2]);
}
}
void ImOrient::CreateSphere()
{
const int SUBDIV = 7;
s_SphTri.clear();
s_SphCol.clear();
const float A[8 * 3] = {1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, -1, -1, 0, 0};
const float B[8 * 3] = {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0};
const float C[8 * 3] = {0, 0, 1, 1, 0, 0, 0, 0, -1, -1, 0, 0, 1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, 1};
const ImU32 COL_A[8] = {0xffffffff, 0xffffff40, 0xff40ff40, 0xff40ffff,
0xffff40ff, 0xffff4040, 0xff404040, 0xff4040ff};
const ImU32 COL_B[8] = {0xffffffff, 0xffffff40, 0xff40ff40, 0xff40ffff,
0xffff40ff, 0xffff4040, 0xff404040, 0xff4040ff};
const ImU32 COL_C[8] = {0xffffffff, 0xffffff40, 0xff40ff40, 0xff40ffff,
0xffff40ff, 0xffff4040, 0xff404040, 0xff4040ff};
int i, j, k, l;
float xa, ya, za, xb, yb, zb, xc, yc, zc, x, y, z, norm, u[3], v[3];
ImU32 col;
for(i = 0; i < 8; ++i)
{
xa = A[3 * i + 0];
ya = A[3 * i + 1];
za = A[3 * i + 2];
xb = B[3 * i + 0];
yb = B[3 * i + 1];
zb = B[3 * i + 2];
xc = C[3 * i + 0];
yc = C[3 * i + 1];
zc = C[3 * i + 2];
for(j = 0; j <= SUBDIV; ++j)
for(k = 0; k <= 2 * (SUBDIV - j); ++k)
{
if(k % 2 == 0)
{
u[0] = ((float)j) / (SUBDIV + 1);
v[0] = ((float)(k / 2)) / (SUBDIV + 1);
u[1] = ((float)(j + 1)) / (SUBDIV + 1);
v[1] = ((float)(k / 2)) / (SUBDIV + 1);
u[2] = ((float)j) / (SUBDIV + 1);
v[2] = ((float)(k / 2 + 1)) / (SUBDIV + 1);
}
else
{
u[0] = ((float)j) / (SUBDIV + 1);
v[0] = ((float)(k / 2 + 1)) / (SUBDIV + 1);
u[1] = ((float)(j + 1)) / (SUBDIV + 1);
v[1] = ((float)(k / 2)) / (SUBDIV + 1);
u[2] = ((float)(j + 1)) / (SUBDIV + 1);
v[2] = ((float)(k / 2 + 1)) / (SUBDIV + 1);
}
for(l = 0; l < 3; ++l)
{
x = (1.0f - u[l] - v[l]) * xa + u[l] * xb + v[l] * xc;
y = (1.0f - u[l] - v[l]) * ya + u[l] * yb + v[l] * yc;
z = (1.0f - u[l] - v[l]) * za + u[l] * zb + v[l] * zc;
norm = sqrtf(x * x + y * y + z * z);
x /= norm;
y /= norm;
z /= norm;
s_SphTri.push_back(ImVec3(x, y, z));
if(u[l] + v[l] > FLT_EPSILON)
col = ColorBlend(COL_A[i], ColorBlend(COL_B[i], COL_C[i], v[l] / (u[l] + v[l])), u[l] + v[l]);
else
col = COL_A[i];
s_SphCol.push_back(col);
}
}
}
s_SphTriProj.clear();
s_SphTriProj.resize(s_SphTri.size());
s_SphColLight.clear();
s_SphColLight.resize(s_SphCol.size());
}
void ImOrient::CreateArrow()
{
const int SUBDIV = 15;
const float CYL_RADIUS = 0.08f;
const float CONE_RADIUS = 0.16f;
const float CONE_LENGTH = 0.25f;
const float ARROW_BGN = -1.1f;
const float ARROW_END = 1.15f;
int i;
for(i = 0; i < 4; ++i)
{
s_ArrowTri[i].clear();
s_ArrowNorm[i].clear();
}
float x0, x1, y0, y1, z0, z1, a0, a1, nx, nn;
for(i = 0; i < SUBDIV; ++i)
{
a0 = 2.0f * float(M_PI) * (float(i)) / SUBDIV;
a1 = 2.0f * float(M_PI) * (float(i + 1)) / SUBDIV;
x0 = ARROW_BGN;
x1 = ARROW_END - CONE_LENGTH;
y0 = cosf(a0);
z0 = sinf(a0);
y1 = cosf(a1);
z1 = sinf(a1);
s_ArrowTri[ARROW_CYL].push_back(ImVec3(x1, CYL_RADIUS * y0, CYL_RADIUS * z0));
s_ArrowTri[ARROW_CYL].push_back(ImVec3(x0, CYL_RADIUS * y0, CYL_RADIUS * z0));
s_ArrowTri[ARROW_CYL].push_back(ImVec3(x0, CYL_RADIUS * y1, CYL_RADIUS * z1));
s_ArrowTri[ARROW_CYL].push_back(ImVec3(x1, CYL_RADIUS * y0, CYL_RADIUS * z0));
s_ArrowTri[ARROW_CYL].push_back(ImVec3(x0, CYL_RADIUS * y1, CYL_RADIUS * z1));
s_ArrowTri[ARROW_CYL].push_back(ImVec3(x1, CYL_RADIUS * y1, CYL_RADIUS * z1));
s_ArrowNorm[ARROW_CYL].push_back(ImVec3(0, y0, z0));
s_ArrowNorm[ARROW_CYL].push_back(ImVec3(0, y0, z0));
s_ArrowNorm[ARROW_CYL].push_back(ImVec3(0, y1, z1));
s_ArrowNorm[ARROW_CYL].push_back(ImVec3(0, y0, z0));
s_ArrowNorm[ARROW_CYL].push_back(ImVec3(0, y1, z1));
s_ArrowNorm[ARROW_CYL].push_back(ImVec3(0, y1, z1));
s_ArrowTri[ARROW_CYL_CAP].push_back(ImVec3(x0, 0, 0));
s_ArrowTri[ARROW_CYL_CAP].push_back(ImVec3(x0, CYL_RADIUS * y1, CYL_RADIUS * z1));
s_ArrowTri[ARROW_CYL_CAP].push_back(ImVec3(x0, CYL_RADIUS * y0, CYL_RADIUS * z0));
s_ArrowNorm[ARROW_CYL_CAP].push_back(ImVec3(-1, 0, 0));
s_ArrowNorm[ARROW_CYL_CAP].push_back(ImVec3(-1, 0, 0));
s_ArrowNorm[ARROW_CYL_CAP].push_back(ImVec3(-1, 0, 0));
x0 = ARROW_END - CONE_LENGTH;
x1 = ARROW_END;
nx = CONE_RADIUS / (x1 - x0);
nn = 1.0f / sqrtf(nx * nx + 1);
s_ArrowTri[ARROW_CONE].push_back(ImVec3(x1, 0, 0));
s_ArrowTri[ARROW_CONE].push_back(ImVec3(x0, CONE_RADIUS * y0, CONE_RADIUS * z0));
s_ArrowTri[ARROW_CONE].push_back(ImVec3(x0, CONE_RADIUS * y1, CONE_RADIUS * z1));
s_ArrowTri[ARROW_CONE].push_back(ImVec3(x1, 0, 0));
s_ArrowTri[ARROW_CONE].push_back(ImVec3(x0, CONE_RADIUS * y1, CONE_RADIUS * z1));
s_ArrowTri[ARROW_CONE].push_back(ImVec3(x1, 0, 0));
s_ArrowNorm[ARROW_CONE].push_back(ImVec3(nn * nx, nn * y0, nn * z0));
s_ArrowNorm[ARROW_CONE].push_back(ImVec3(nn * nx, nn * y0, nn * z0));
s_ArrowNorm[ARROW_CONE].push_back(ImVec3(nn * nx, nn * y1, nn * z1));
s_ArrowNorm[ARROW_CONE].push_back(ImVec3(nn * nx, nn * y0, nn * z0));
s_ArrowNorm[ARROW_CONE].push_back(ImVec3(nn * nx, nn * y1, nn * z1));
s_ArrowNorm[ARROW_CONE].push_back(ImVec3(nn * nx, nn * y1, nn * z1));
s_ArrowTri[ARROW_CONE_CAP].push_back(ImVec3(x0, 0, 0));
s_ArrowTri[ARROW_CONE_CAP].push_back(ImVec3(x0, CONE_RADIUS * y1, CONE_RADIUS * z1));
s_ArrowTri[ARROW_CONE_CAP].push_back(ImVec3(x0, CONE_RADIUS * y0, CONE_RADIUS * z0));
s_ArrowNorm[ARROW_CONE_CAP].push_back(ImVec3(-1, 0, 0));
s_ArrowNorm[ARROW_CONE_CAP].push_back(ImVec3(-1, 0, 0));
s_ArrowNorm[ARROW_CONE_CAP].push_back(ImVec3(-1, 0, 0));
}
for(i = 0; i < 4; ++i)
{
s_ArrowTriProj[i].clear();
s_ArrowTriProj[i].resize(s_ArrowTri[i].size());
s_ArrowColLight[i].clear();
s_ArrowColLight[i].resize(s_ArrowTri[i].size());
}
}
void ImOrient::ConvertToAxisAngle()
{
if(fabs(Qt.w) > (1.0 + FLT_EPSILON))
{
//Axis.x = Axis.y = Axis.z = 0; // no, keep the previous value
Angle = 0;
}
else
{
float a;
if(Qt.w >= 1.0f)
a = 0.0f; // and keep V
else if(Qt.w <= -1.0f)
a = float(M_PI); // and keep V
else if(fabsf(Qt.x * Qt.x + Qt.y * Qt.y + Qt.z * Qt.z + Qt.w * Qt.w) < (FLT_EPSILON * FLT_EPSILON))
a = 0.0f;
else
{
a = (float)acos(Qt.w);
if(a * Angle < 0) // Preserve the sign of Angle
a = -a;
float f = 1.0f / (float)sin(a);
Axis.x = Qt.x * f;
Axis.y = Qt.y * f;
Axis.z = Qt.z * f;
}
Angle = 2.0f * a;
}
Angle = ImRadToDeg(Angle);
if(fabsf(Angle) < FLT_EPSILON && fabsf(Axis.x * Axis.x + Axis.y * Axis.y + Axis.z * Axis.z) < FLT_EPSILON * FLT_EPSILON)
Axis.x = FLT_MIN; // all components cannot be null
}
void ImOrient::ConvertFromAxisAngle()
{
float n = Axis.x * Axis.x + Axis.y * Axis.y + Axis.z * Axis.z;
if(fabsf(n) > (FLT_EPSILON * FLT_EPSILON))
{
float f = 0.5f * ImDegToRad(Angle);
Qt.w = (float)cos(f);
f = (float)sin(f);
Qt.x = Axis.x * f;
Qt.y = Axis.y * f;
Qt.z = Axis.z * f;
}
else
{
Qt.w = 1.0;
Qt.x = Qt.y = Qt.z = 0.0;
}
}
/*
void ImGui_Orient::CopyToVar()
{
if( m_StructProxy!=NULL )
{
if( m_StructProxy->m_StructSetCallback!=NULL )
{
if( m_IsFloat )
{
if( m_IsDir )
{
float d[] = {1, 0, 0};
ApplyQuat(d+0, d+1, d+2, 1, 0, 0, (float)Qt.x, (float)Qt.y, (float)Qt.z, (float)Qt.w);
float l = (float)sqrt(Dir.x*Dir.x + Dir.y*Dir.y + Dir.z*Dir.z);
d[0] *= l; d[1] *= l; d[2] *= l;
Dir.x = d[0]; Dir.y = d[1]; Dir.z = d[2]; // update also Dir.x,Dir.y,Dir.z
m_StructProxy->m_StructSetCallback(d, m_StructProxy->m_StructClientData);
}
else
{
float q[] = { (float)Qt.x, (float)Qt.y, (float)Qt.z, (float)Qt.w };
m_StructProxy->m_StructSetCallback(q, m_StructProxy->m_StructClientData);
}
}
}
else if( m_StructProxy->m_StructData!=NULL )
{
if( m_IsFloat )
{
if( m_IsDir )
{
float *d = static_cast<float *>(m_StructProxy->m_StructData);
ApplyQuat(d+0, d+1, d+2, 1, 0, 0, (float)Qt.x, (float)Qt.y, (float)Qt.z, (float)Qt.w);
float l = (float)sqrt(Dir.x*Dir.x + Dir.y*Dir.y + Dir.z*Dir.z);
d[0] *= l; d[1] *= l; d[2] *= l;
Dir.x = d[0]; Dir.y = d[1]; Dir.z = d[2]; // update also Dir.x,Dir.y,Dir.z
}
else
{
float *q = static_cast<float *>(m_StructProxy->m_StructData);
q[0] = (float)Qt.x; q[1] = (float)Qt.y; q[2] = (float)Qt.z; q[3] = (float)Qt.w;
}
}
else
{
if( m_IsDir )
{
float *dd = static_cast<float *>(m_StructProxy->m_StructData);
float d[] = {1, 0, 0};
ApplyQuat(d+0, d+1, d+2, 1, 0, 0, (float)Qt.x, (float)Qt.y, (float)Qt.z, (float)Qt.w);
float l = sqrt(Dir.x*Dir.x + Dir.y*Dir.y + Dir.z*Dir.z);
dd[0] = l*d[0]; dd[1] = l*d[1]; dd[2] = l*d[2];
Dir.x = dd[0]; Dir.y = dd[1]; Dir.z = dd[2]; // update also Dir.x,Dir.y,Dir.z
}
else
{
float *q = static_cast<float *>(m_StructProxy->m_StructData);
q[0] = Qt.x; q[1] = Qt.y; q[2] = Qt.z; q[3] = Qt.w;
}
}
}
}
}
*/
ImU32 ImOrient::ColorBlend(ImU32 _Color1, ImU32 _Color2, float sigma)
{
ImColor color1(_Color1);
ImColor color2(_Color2);
float invSigma = 1.0f - sigma;
color1 =
ImColor((color1.Value.x * invSigma) + (color2.Value.x * sigma), (color1.Value.y * invSigma) + (color2.Value.y * sigma),
(color1.Value.z * invSigma) + (color2.Value.z * sigma), (color1.Value.w * invSigma) + (color2.Value.w * sigma));
return color1;
}
void ImOrient::QuatFromAxisAngle(ImQuat& out, const ImVec3& axis, float angle)
{
float n = axis.x * axis.x + axis.y * axis.y + axis.z * axis.z;
if(fabs(n) > FLT_EPSILON)
{
float f = 0.5f * angle;
out.w = (float)cos(f);
f = (float)(sin(f) / sqrt(n));
out.x = axis.x * f;
out.y = axis.y * f;
out.z = axis.z * f;
}
else
{
out.w = 1.0;
out.x = out.y = out.z = 0.0;
}
}
void ImOrient::QuatFromDir(ImQuat& out, const ImVec3& dir)
{
// compute a quaternion that rotates (1,0,0) to (dx,dy,dz)
float dn = sqrtf(dir.x * dir.x + dir.y * dir.y + dir.z * dir.z);
if(dn < FLT_EPSILON * FLT_EPSILON)
{
out.x = out.y = out.z = 0;
out.w = 1;
}
else
{
ImVec3 rotAxis = {0, -dir.z, dir.y};
if(rotAxis.x * rotAxis.x + rotAxis.y * rotAxis.y + rotAxis.z * rotAxis.z < (FLT_EPSILON * FLT_EPSILON))
{
rotAxis.x = rotAxis.y = 0;
rotAxis.z = 1;
}
float rotAngle = (float)acos(dir.x / dn);
ImQuat rotQuat;
QuatFromAxisAngle(rotQuat, rotAxis, rotAngle);
out.x = rotQuat.x;
out.y = rotQuat.y;
out.z = rotQuat.z;
out.w = rotQuat.w;
}
}

View file

@ -0,0 +1,254 @@
/*
* Copyright (c) 2019-2023, 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) 2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <algorithm>
#include <cfloat>
#include <cmath>
#ifndef M_PI
#define M_PI 3.1415926535
#endif
/* @DOC_START
# struct ImOrient
> brief This is a really nice implementation of an orientation widget; all due respect to the original author ;)
This is a port of the AntTweakBar orientation widget, which is a 3D orientation widget that allows the user to specify a
3D orientation using a quaternion, axis-angle, or direction vector. It is a very useful widget for 3D applications.
--- @DOC_END ------------------------------------------------------- */
/**
Notes from: www.github.com/cmaughan
Ported from AntTweakBar
Dependencies kept to a minimum. I basically vectorized the original code, added a few math types, cleaned things up and
made it clearer what the maths was doing.
I tried to make it more imgui-like, and removed all the excess stuff not needed here. This still needs work.
I also added triangle culling because ImGui doesn't support winding clip
The widget works by transforming the 3D object to screen space and clipping the triangles. This makes it work with any
imgui back end, without modifications to the renderers.
\todo More cleanup.
\todo Figure out what ShowDir is for.
\todo Test direction vectors more
*/
// --------------------------
// Firstly, a little math, missing from ImGui but needed for this widget
// A Vec3, Matrix 3x3, Dot & Cross products, A Quaternion. Some helper functions, bare minimum
struct ImVec3
{
float x, y, z;
ImVec3() { x = y = z = 0.0f; }
ImVec3(float _x, float _y, float _z)
{
x = _x;
y = _y;
z = _z;
}
ImVec3 RotY() const { return ImVec3(-z, y, x); }
ImVec3 RotZ() const { return ImVec3(-y, x, z); }
ImVec3 Cross(const ImVec3& b) const { return ImVec3(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x); }
float Dot(const ImVec3& b) const { return x * b.x + y * b.y + z * b.z; }
ImVec3 Mult(float val) const { return ImVec3(x * val, y * val, z * val); }
ImVec3 Div(float val) const { return ImVec3(x / val, y / val, z / val); }
float Length() const { return sqrtf(x * x + y * y + z * z); }
#ifdef IM_VEC3_CLASS_EXTRA // Define constructor and implicit cast operators in imconfig.h to convert back<>forth from your math types and ImVec2.
IM_VEC3_CLASS_EXTRA
#endif
};
// Added to the existing ImVec2 vector
inline ImVec2 ImVec2Subtract(const ImVec2& left, const ImVec2& right)
{
return ImVec2(left.x - right.x, left.y - right.y);
}
inline float ImVec2Cross(const ImVec2& left, const ImVec2& right)
{
return (left.x * right.y) - (left.y * right.x);
}
struct ImQuat
{
float x, y, z, w;
ImQuat()
{
x = y = z = 0.0f;
w = 1.0f;
}
ImQuat(float _x, float _y, float _z, float _w)
{
x = _x;
y = _y;
z = _z;
w = _w;
}
ImQuat Div(float val) { return ImQuat(x / val, y / val, z / val, w / val); }
ImVec3 Rotate(const ImVec3& dir)
{
float ps = -x * dir.x - y * dir.y - z * dir.z;
float px = w * dir.x + y * dir.z - z * dir.y;
float py = w * dir.y + z * dir.x - x * dir.z;
float pz = w * dir.z + x * dir.y - y * dir.x;
return ImVec3(-ps * x + px * w - py * z + pz * y, -ps * y + py * w - pz * x + px * z, -ps * z + pz * w - px * y + py * x);
}
ImQuat Mult(const ImQuat& q2)
{
ImQuat out;
out.x = w * q2.x + x * q2.w + y * q2.z - z * q2.y;
out.y = w * q2.y + y * q2.w + z * q2.x - x * q2.z;
out.z = w * q2.z + z * q2.w + x * q2.y - y * q2.x;
out.w = w * q2.w - (x * q2.x + y * q2.y + z * q2.z);
return out;
}
#ifdef IM_QUAT_CLASS_EXTRA // Define constructor and implicit cast operators in imconfig.h to convert back<>forth from your math types and ImVec2.
IM_QUAT_CLASS_EXTRA
#endif
};
// Matrix used to allow user to specify axis orientation
struct ImMat3x3
{
float m[3][3];
ImMat3x3()
{
for(int x = 0; x < 3; x++)
{
for(int y = 0; y < 3; y++)
{
m[y][x] = (x == y) ? 1.0f : 0.0f;
}
}
}
ImVec3 Transform(const ImVec3& vec)
{
ImVec3 out;
out.x = m[0][0] * vec.x + m[1][0] * vec.y + m[2][0] * vec.z;
out.y = m[0][1] * vec.x + m[1][1] * vec.y + m[2][1] * vec.z;
out.z = m[0][2] * vec.x + m[1][2] * vec.y + m[2][2] * vec.z;
return out;
}
ImVec3 TransformInv(const ImVec3& vec)
{
ImVec3 out;
out.x = m[0][0] * vec.x + m[0][1] * vec.y + m[0][2] * vec.z;
out.y = m[1][0] * vec.x + m[1][1] * vec.y + m[1][2] * vec.z;
out.z = m[2][0] * vec.x + m[2][1] * vec.y + m[2][2] * vec.z;
return out;
}
};
inline float ImDegToRad(float degree)
{
return degree * (float(M_PI) / 180.0f);
}
inline float ImRadToDeg(float radian)
{
return radian * (180.0f / float(M_PI));
}
// The data structure that holds the orientation among other things
struct ImOrient
{
ImQuat Qt; // Quaternion value
ImVec3 Axis; // Axis and Angle
float Angle = 0.0f;
ImVec3 Dir; // Dir value set when used as a direction
bool m_AAMode = false; // Axis & angle mode
bool m_IsDir = false; // Mapped to a dir vector instead of a quat
ImVec3 m_ShowDir; // CM: Not sure what this is all about?
ImU32 m_DirColor{}; // Direction vector color
ImMat3x3 AxisTransform; // Transform to required axis frame
// For the geometry
enum EArrowParts
{
ARROW_CONE,
ARROW_CONE_CAP,
ARROW_CYL,
ARROW_CYL_CAP
};
static ImVector<ImVec3> s_SphTri;
static ImVector<ImU32> s_SphCol;
static ImVector<ImVec2> s_SphTriProj;
static ImVector<ImU32> s_SphColLight;
static ImVector<ImVec3> s_ArrowTri[4];
static ImVector<ImVec2> s_ArrowTriProj[4];
static ImVector<ImVec3> s_ArrowNorm[4];
static ImVector<ImU32> s_ArrowColLight[4];
static void CreateSphere();
static void CreateArrow();
IMGUI_API bool Draw(const char* label, bool show_info = false);
IMGUI_API void DrawTriangles(ImDrawList* draw_list,
const ImVec2& offset,
const ImVector<ImVec2>& triProj,
const ImVector<ImU32>& colLight,
int numVertices,
float cullDir);
IMGUI_API void ConvertToAxisAngle();
IMGUI_API void ConvertFromAxisAngle();
// Quaternions
inline float QuatD(float w, float h) { return (float)std::min(std::abs(w), std::abs(h)) - 4.0f; }
inline float QuatPX(float x, float w, float h) { return (x * 0.5f * QuatD(w, h) + w * 0.5f + 0.5f); }
inline float QuatPY(float y, float w, float h) { return (-y * 0.5f * QuatD(w, h) + h * 0.5f - 0.5f); }
inline float QuatIX(int x, float w, float h) { return (2.0f * x - w - 1.0f) / QuatD(w, h); }
inline float QuatIY(int y, float w, float h) { return (-2.0f * y + h - 1.0f) / QuatD(w, h); }
IMGUI_API void QuatFromDir(ImQuat& quat, const ImVec3& dir);
IMGUI_API static void QuatFromAxisAngle(ImQuat& qt, const ImVec3& axis, float angle);
// Useful colors
IMGUI_API static ImU32 ColorBlend(ImU32 _Color1, ImU32 _Color2, float _S);
typedef unsigned int color32;
const ImU32 COLOR32_BLACK = 0xff000000; // Black
const ImU32 COLOR32_WHITE = 0xffffffff; // White
const ImU32 COLOR32_ZERO = 0x00000000; // Zero
const ImU32 COLOR32_RED = 0xffff0000; // Red
const ImU32 COLOR32_GREEN = 0xff00ff00; // Green
const ImU32 COLOR32_BLUE = 0xff0000ff; // Blue
const int GIZMO_SIZE = 100;
};
// The API
namespace ImGui {
IMGUI_API bool QuaternionGizmo(const char* label, ImQuat& quat);
IMGUI_API bool AxisAngleGizmo(const char* label, ImVec3& axis, float& angle);
IMGUI_API bool DirectionGizmo(const char* label, float* dir, bool flip = false, bool show_info = false);
}; // namespace ImGui