Inital commit v1.0
This commit is contained in:
34
include/GPData.h
Normal file
34
include/GPData.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace gpgui {
|
||||||
|
namespace data {
|
||||||
|
|
||||||
|
typedef std::vector<float> VertexData;
|
||||||
|
|
||||||
|
struct Color {
|
||||||
|
std::uint8_t red, green, blue, alpha = 255;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DrawData {
|
||||||
|
std::uint32_t vao, vbo;
|
||||||
|
std::size_t vertexCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::uint8_t EMPTY_TAB = 0xF;
|
||||||
|
static const std::uint8_t EMPTY_NOTE = 0xFF;
|
||||||
|
|
||||||
|
VertexData GetCircleData(float centerX, float centerY, float radius, float color, int precision);
|
||||||
|
VertexData GetRectData(float x, float y, float dx, float dy, float color);
|
||||||
|
|
||||||
|
DrawData GetDrawData(const VertexData& vertexData);
|
||||||
|
void UpdateData(DrawData& buffer, const VertexData& newData);
|
||||||
|
|
||||||
|
void DrawVertexData(const DrawData& vertexData);
|
||||||
|
|
||||||
|
float GetIntColor(const Color& color);
|
||||||
|
|
||||||
|
} // namespace data
|
||||||
|
} // namespace gpgui
|
||||||
10
include/GPGui.h
Normal file
10
include/GPGui.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace gpgui {
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
void Render();
|
||||||
|
void Init();
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace gpgui
|
||||||
68
include/GPMusic.h
Normal file
68
include/GPMusic.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace gpgui {
|
||||||
|
|
||||||
|
namespace save {
|
||||||
|
|
||||||
|
struct ChordSave;
|
||||||
|
|
||||||
|
} // namespace save
|
||||||
|
|
||||||
|
namespace music {
|
||||||
|
|
||||||
|
enum Note : std::uint8_t {
|
||||||
|
A = 0,
|
||||||
|
Bb,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
Db,
|
||||||
|
D,
|
||||||
|
Eb,
|
||||||
|
E,
|
||||||
|
F,
|
||||||
|
Gb,
|
||||||
|
G,
|
||||||
|
Ab,
|
||||||
|
|
||||||
|
TOTAL
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ChordType : std::uint8_t {
|
||||||
|
Major = 0,
|
||||||
|
Minor,
|
||||||
|
Dim,
|
||||||
|
Major7,
|
||||||
|
Minor7,
|
||||||
|
Sus,
|
||||||
|
|
||||||
|
COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::array<int, 6> Tab;
|
||||||
|
typedef std::array<Note, 4> Chord;
|
||||||
|
typedef std::array<std::uint8_t, 4> ChordOffsets;
|
||||||
|
|
||||||
|
Note GetNote(std::uint8_t touche);
|
||||||
|
std::uint8_t GetOctave(std::uint8_t touche);
|
||||||
|
|
||||||
|
std::string ToString(Note note);
|
||||||
|
std::string ToString(ChordType chord);
|
||||||
|
std::string ToString(const Tab& tab);
|
||||||
|
std::string ToString(const save::ChordSave& tab);
|
||||||
|
|
||||||
|
int GetStringOffset(int string);
|
||||||
|
|
||||||
|
Tab FindChord(const Chord& chord, int capo);
|
||||||
|
Tab FindFakeChord(const Chord& chord, int capo, int pCorde = 0);
|
||||||
|
|
||||||
|
Tab FindPianoChord(const ChordOffsets& notes, int capo = 0, int fretThreshold = 5);
|
||||||
|
|
||||||
|
ChordOffsets GetChordOffsets(Note note, ChordType type);
|
||||||
|
Chord GetChord(Note note, ChordType type);
|
||||||
|
|
||||||
|
} // namespace music
|
||||||
|
} // namespace gpgui
|
||||||
23
include/GPRenderer.h
Normal file
23
include/GPRenderer.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace gpgui {
|
||||||
|
namespace renderer {
|
||||||
|
|
||||||
|
void InitRendering();
|
||||||
|
|
||||||
|
void DrawWidgets();
|
||||||
|
void DrawStrings();
|
||||||
|
|
||||||
|
bool IsKeyHighlited(int key);
|
||||||
|
void SetKeyHighlight(int key, bool highlight);
|
||||||
|
void ClearKeyboard();
|
||||||
|
|
||||||
|
void UpdateBuffers();
|
||||||
|
|
||||||
|
void ClearTab();
|
||||||
|
void SetTab(int tab, int fret);
|
||||||
|
|
||||||
|
void SetCapoPos(int capo);
|
||||||
|
|
||||||
|
} // namespace renderer
|
||||||
|
} // namespace gpgui
|
||||||
33
include/GPSave.h
Normal file
33
include/GPSave.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "GPMusic.h"
|
||||||
|
|
||||||
|
namespace gpgui {
|
||||||
|
namespace save {
|
||||||
|
|
||||||
|
typedef std::uint8_t CapoPosType;
|
||||||
|
|
||||||
|
struct ChordSave {
|
||||||
|
music::Note note : 4;
|
||||||
|
music::ChordType type : 3;
|
||||||
|
bool guitaroPiano : 1;
|
||||||
|
std::uint8_t octave : 2;
|
||||||
|
std::uint8_t inversion : 2;
|
||||||
|
std::uint8_t fretMax : 4;
|
||||||
|
|
||||||
|
ChordSave() : note(music::Note::TOTAL) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Song {
|
||||||
|
std::string title;
|
||||||
|
CapoPosType capo;
|
||||||
|
std::vector<ChordSave> chords;
|
||||||
|
|
||||||
|
Song(const std::string& songTitle, CapoPosType songCapo) : title(songTitle), capo(songCapo) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void SaveSongToFile(const Song& save, const std::string& fileName);
|
||||||
|
Song LoadSongFromFile(const std::string& filePath);
|
||||||
|
|
||||||
|
} // namespace save
|
||||||
|
} // namespace gpgui
|
||||||
32
include/ShaderProgram.h
Executable file
32
include/ShaderProgram.h
Executable file
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace gpgui {
|
||||||
|
|
||||||
|
class ShaderProgram {
|
||||||
|
public:
|
||||||
|
ShaderProgram();
|
||||||
|
virtual ~ShaderProgram();
|
||||||
|
|
||||||
|
void Start() const;
|
||||||
|
void Stop() const;
|
||||||
|
|
||||||
|
void LoadProgramFile(const std::string& vertexFile, const std::string& fragmentFile);
|
||||||
|
void LoadProgram(const std::string& vertexSource, const std::string& fragmentSource);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void GetAllUniformLocation() = 0;
|
||||||
|
|
||||||
|
void CleanUp() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int m_ProgramID;
|
||||||
|
unsigned int m_VertexShaderID;
|
||||||
|
unsigned int m_FragmentShaderID;
|
||||||
|
|
||||||
|
unsigned int LoadShaderFromFile(const std::string& file, unsigned int type);
|
||||||
|
unsigned int LoadShader(const std::string& source, unsigned type);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gpgui
|
||||||
662
libs/imgui/backends/imgui_impl_glfw.cpp
Normal file
662
libs/imgui/backends/imgui_impl_glfw.cpp
Normal file
@@ -0,0 +1,662 @@
|
|||||||
|
// dear imgui: Platform Backend for GLFW
|
||||||
|
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..)
|
||||||
|
// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
|
||||||
|
// (Requires: GLFW 3.1+)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Platform: Clipboard support.
|
||||||
|
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||||
|
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||||
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||||
|
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2022-04-30: Inputs: Fixed ImGui_ImplGlfw_TranslateUntranslatedKey() for lower case letters on OSX.
|
||||||
|
// 2022-03-23: Inputs: Fixed a regression in 1.87 which resulted in keyboard modifiers events being reported incorrectly on Linux/X11.
|
||||||
|
// 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after initializing backend.
|
||||||
|
// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
|
||||||
|
// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[].
|
||||||
|
// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
|
||||||
|
// 2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates.
|
||||||
|
// 2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback().
|
||||||
|
// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
|
||||||
|
// 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API.
|
||||||
|
// 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback().
|
||||||
|
// 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback().
|
||||||
|
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||||
|
// 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors.
|
||||||
|
// 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor).
|
||||||
|
// 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown.
|
||||||
|
// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
|
||||||
|
// 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter().
|
||||||
|
// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.
|
||||||
|
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
|
||||||
|
// 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them.
|
||||||
|
// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
|
||||||
|
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
|
||||||
|
// 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples.
|
||||||
|
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
|
||||||
|
// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()).
|
||||||
|
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||||
|
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
|
||||||
|
// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set.
|
||||||
|
// 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
|
||||||
|
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
|
||||||
|
// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
|
||||||
|
// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
|
||||||
|
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_impl_glfw.h"
|
||||||
|
|
||||||
|
// Clang warnings with -Weverything
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
|
||||||
|
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||||
|
#if __has_warning("-Wzero-as-null-pointer-constant")
|
||||||
|
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// GLFW
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#undef APIENTRY
|
||||||
|
#define GLFW_EXPOSE_NATIVE_WIN32
|
||||||
|
#include <GLFW/glfw3native.h> // for glfwGetWin32Window
|
||||||
|
#endif
|
||||||
|
#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released?
|
||||||
|
#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR
|
||||||
|
#else
|
||||||
|
#define GLFW_HAS_NEW_CURSORS (0)
|
||||||
|
#endif
|
||||||
|
#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetGamepadState() new api
|
||||||
|
#define GLFW_HAS_GET_KEY_NAME (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwGetKeyName()
|
||||||
|
|
||||||
|
// GLFW data
|
||||||
|
enum GlfwClientApi
|
||||||
|
{
|
||||||
|
GlfwClientApi_Unknown,
|
||||||
|
GlfwClientApi_OpenGL,
|
||||||
|
GlfwClientApi_Vulkan
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImGui_ImplGlfw_Data
|
||||||
|
{
|
||||||
|
GLFWwindow* Window;
|
||||||
|
GlfwClientApi ClientApi;
|
||||||
|
double Time;
|
||||||
|
GLFWwindow* MouseWindow;
|
||||||
|
GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT];
|
||||||
|
ImVec2 LastValidMousePos;
|
||||||
|
bool InstalledCallbacks;
|
||||||
|
|
||||||
|
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
|
||||||
|
GLFWwindowfocusfun PrevUserCallbackWindowFocus;
|
||||||
|
GLFWcursorposfun PrevUserCallbackCursorPos;
|
||||||
|
GLFWcursorenterfun PrevUserCallbackCursorEnter;
|
||||||
|
GLFWmousebuttonfun PrevUserCallbackMousebutton;
|
||||||
|
GLFWscrollfun PrevUserCallbackScroll;
|
||||||
|
GLFWkeyfun PrevUserCallbackKey;
|
||||||
|
GLFWcharfun PrevUserCallbackChar;
|
||||||
|
GLFWmonitorfun PrevUserCallbackMonitor;
|
||||||
|
|
||||||
|
ImGui_ImplGlfw_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
|
||||||
|
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||||
|
// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
|
||||||
|
// - Because glfwPollEvents() process all windows and some events may be called outside of it, you will need to register your own callbacks
|
||||||
|
// (passing install_callbacks=false in ImGui_ImplGlfw_InitXXX functions), set the current dear imgui context and then call our callbacks.
|
||||||
|
// - Otherwise we may need to store a GLFWWindow* -> ImGuiContext* map and handle this in the backend, adding a little bit of extra complexity to it.
|
||||||
|
// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
|
||||||
|
static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData()
|
||||||
|
{
|
||||||
|
return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data)
|
||||||
|
{
|
||||||
|
return glfwGetClipboardString((GLFWwindow*)user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text)
|
||||||
|
{
|
||||||
|
glfwSetClipboardString((GLFWwindow*)user_data, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key)
|
||||||
|
{
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case GLFW_KEY_TAB: return ImGuiKey_Tab;
|
||||||
|
case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow;
|
||||||
|
case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow;
|
||||||
|
case GLFW_KEY_UP: return ImGuiKey_UpArrow;
|
||||||
|
case GLFW_KEY_DOWN: return ImGuiKey_DownArrow;
|
||||||
|
case GLFW_KEY_PAGE_UP: return ImGuiKey_PageUp;
|
||||||
|
case GLFW_KEY_PAGE_DOWN: return ImGuiKey_PageDown;
|
||||||
|
case GLFW_KEY_HOME: return ImGuiKey_Home;
|
||||||
|
case GLFW_KEY_END: return ImGuiKey_End;
|
||||||
|
case GLFW_KEY_INSERT: return ImGuiKey_Insert;
|
||||||
|
case GLFW_KEY_DELETE: return ImGuiKey_Delete;
|
||||||
|
case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace;
|
||||||
|
case GLFW_KEY_SPACE: return ImGuiKey_Space;
|
||||||
|
case GLFW_KEY_ENTER: return ImGuiKey_Enter;
|
||||||
|
case GLFW_KEY_ESCAPE: return ImGuiKey_Escape;
|
||||||
|
case GLFW_KEY_APOSTROPHE: return ImGuiKey_Apostrophe;
|
||||||
|
case GLFW_KEY_COMMA: return ImGuiKey_Comma;
|
||||||
|
case GLFW_KEY_MINUS: return ImGuiKey_Minus;
|
||||||
|
case GLFW_KEY_PERIOD: return ImGuiKey_Period;
|
||||||
|
case GLFW_KEY_SLASH: return ImGuiKey_Slash;
|
||||||
|
case GLFW_KEY_SEMICOLON: return ImGuiKey_Semicolon;
|
||||||
|
case GLFW_KEY_EQUAL: return ImGuiKey_Equal;
|
||||||
|
case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket;
|
||||||
|
case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash;
|
||||||
|
case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket;
|
||||||
|
case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent;
|
||||||
|
case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock;
|
||||||
|
case GLFW_KEY_SCROLL_LOCK: return ImGuiKey_ScrollLock;
|
||||||
|
case GLFW_KEY_NUM_LOCK: return ImGuiKey_NumLock;
|
||||||
|
case GLFW_KEY_PRINT_SCREEN: return ImGuiKey_PrintScreen;
|
||||||
|
case GLFW_KEY_PAUSE: return ImGuiKey_Pause;
|
||||||
|
case GLFW_KEY_KP_0: return ImGuiKey_Keypad0;
|
||||||
|
case GLFW_KEY_KP_1: return ImGuiKey_Keypad1;
|
||||||
|
case GLFW_KEY_KP_2: return ImGuiKey_Keypad2;
|
||||||
|
case GLFW_KEY_KP_3: return ImGuiKey_Keypad3;
|
||||||
|
case GLFW_KEY_KP_4: return ImGuiKey_Keypad4;
|
||||||
|
case GLFW_KEY_KP_5: return ImGuiKey_Keypad5;
|
||||||
|
case GLFW_KEY_KP_6: return ImGuiKey_Keypad6;
|
||||||
|
case GLFW_KEY_KP_7: return ImGuiKey_Keypad7;
|
||||||
|
case GLFW_KEY_KP_8: return ImGuiKey_Keypad8;
|
||||||
|
case GLFW_KEY_KP_9: return ImGuiKey_Keypad9;
|
||||||
|
case GLFW_KEY_KP_DECIMAL: return ImGuiKey_KeypadDecimal;
|
||||||
|
case GLFW_KEY_KP_DIVIDE: return ImGuiKey_KeypadDivide;
|
||||||
|
case GLFW_KEY_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
|
||||||
|
case GLFW_KEY_KP_SUBTRACT: return ImGuiKey_KeypadSubtract;
|
||||||
|
case GLFW_KEY_KP_ADD: return ImGuiKey_KeypadAdd;
|
||||||
|
case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter;
|
||||||
|
case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual;
|
||||||
|
case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift;
|
||||||
|
case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftCtrl;
|
||||||
|
case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt;
|
||||||
|
case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper;
|
||||||
|
case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift;
|
||||||
|
case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightCtrl;
|
||||||
|
case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt;
|
||||||
|
case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper;
|
||||||
|
case GLFW_KEY_MENU: return ImGuiKey_Menu;
|
||||||
|
case GLFW_KEY_0: return ImGuiKey_0;
|
||||||
|
case GLFW_KEY_1: return ImGuiKey_1;
|
||||||
|
case GLFW_KEY_2: return ImGuiKey_2;
|
||||||
|
case GLFW_KEY_3: return ImGuiKey_3;
|
||||||
|
case GLFW_KEY_4: return ImGuiKey_4;
|
||||||
|
case GLFW_KEY_5: return ImGuiKey_5;
|
||||||
|
case GLFW_KEY_6: return ImGuiKey_6;
|
||||||
|
case GLFW_KEY_7: return ImGuiKey_7;
|
||||||
|
case GLFW_KEY_8: return ImGuiKey_8;
|
||||||
|
case GLFW_KEY_9: return ImGuiKey_9;
|
||||||
|
case GLFW_KEY_A: return ImGuiKey_A;
|
||||||
|
case GLFW_KEY_B: return ImGuiKey_B;
|
||||||
|
case GLFW_KEY_C: return ImGuiKey_C;
|
||||||
|
case GLFW_KEY_D: return ImGuiKey_D;
|
||||||
|
case GLFW_KEY_E: return ImGuiKey_E;
|
||||||
|
case GLFW_KEY_F: return ImGuiKey_F;
|
||||||
|
case GLFW_KEY_G: return ImGuiKey_G;
|
||||||
|
case GLFW_KEY_H: return ImGuiKey_H;
|
||||||
|
case GLFW_KEY_I: return ImGuiKey_I;
|
||||||
|
case GLFW_KEY_J: return ImGuiKey_J;
|
||||||
|
case GLFW_KEY_K: return ImGuiKey_K;
|
||||||
|
case GLFW_KEY_L: return ImGuiKey_L;
|
||||||
|
case GLFW_KEY_M: return ImGuiKey_M;
|
||||||
|
case GLFW_KEY_N: return ImGuiKey_N;
|
||||||
|
case GLFW_KEY_O: return ImGuiKey_O;
|
||||||
|
case GLFW_KEY_P: return ImGuiKey_P;
|
||||||
|
case GLFW_KEY_Q: return ImGuiKey_Q;
|
||||||
|
case GLFW_KEY_R: return ImGuiKey_R;
|
||||||
|
case GLFW_KEY_S: return ImGuiKey_S;
|
||||||
|
case GLFW_KEY_T: return ImGuiKey_T;
|
||||||
|
case GLFW_KEY_U: return ImGuiKey_U;
|
||||||
|
case GLFW_KEY_V: return ImGuiKey_V;
|
||||||
|
case GLFW_KEY_W: return ImGuiKey_W;
|
||||||
|
case GLFW_KEY_X: return ImGuiKey_X;
|
||||||
|
case GLFW_KEY_Y: return ImGuiKey_Y;
|
||||||
|
case GLFW_KEY_Z: return ImGuiKey_Z;
|
||||||
|
case GLFW_KEY_F1: return ImGuiKey_F1;
|
||||||
|
case GLFW_KEY_F2: return ImGuiKey_F2;
|
||||||
|
case GLFW_KEY_F3: return ImGuiKey_F3;
|
||||||
|
case GLFW_KEY_F4: return ImGuiKey_F4;
|
||||||
|
case GLFW_KEY_F5: return ImGuiKey_F5;
|
||||||
|
case GLFW_KEY_F6: return ImGuiKey_F6;
|
||||||
|
case GLFW_KEY_F7: return ImGuiKey_F7;
|
||||||
|
case GLFW_KEY_F8: return ImGuiKey_F8;
|
||||||
|
case GLFW_KEY_F9: return ImGuiKey_F9;
|
||||||
|
case GLFW_KEY_F10: return ImGuiKey_F10;
|
||||||
|
case GLFW_KEY_F11: return ImGuiKey_F11;
|
||||||
|
case GLFW_KEY_F12: return ImGuiKey_F12;
|
||||||
|
default: return ImGuiKey_None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ImGui_ImplGlfw_KeyToModifier(int key)
|
||||||
|
{
|
||||||
|
if (key == GLFW_KEY_LEFT_CONTROL || key == GLFW_KEY_RIGHT_CONTROL)
|
||||||
|
return GLFW_MOD_CONTROL;
|
||||||
|
if (key == GLFW_KEY_LEFT_SHIFT || key == GLFW_KEY_RIGHT_SHIFT)
|
||||||
|
return GLFW_MOD_SHIFT;
|
||||||
|
if (key == GLFW_KEY_LEFT_ALT || key == GLFW_KEY_RIGHT_ALT)
|
||||||
|
return GLFW_MOD_ALT;
|
||||||
|
if (key == GLFW_KEY_LEFT_SUPER || key == GLFW_KEY_RIGHT_SUPER)
|
||||||
|
return GLFW_MOD_SUPER;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplGlfw_UpdateKeyModifiers(int mods)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddKeyEvent(ImGuiKey_ModCtrl, (mods & GLFW_MOD_CONTROL) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiKey_ModShift, (mods & GLFW_MOD_SHIFT) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiKey_ModAlt, (mods & GLFW_MOD_ALT) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiKey_ModSuper, (mods & GLFW_MOD_SUPER) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
|
||||||
|
{
|
||||||
|
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
|
||||||
|
if (bd->PrevUserCallbackMousebutton != NULL && window == bd->Window)
|
||||||
|
bd->PrevUserCallbackMousebutton(window, button, action, mods);
|
||||||
|
|
||||||
|
ImGui_ImplGlfw_UpdateKeyModifiers(mods);
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if (button >= 0 && button < ImGuiMouseButton_COUNT)
|
||||||
|
io.AddMouseButtonEvent(button, action == GLFW_PRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
|
||||||
|
{
|
||||||
|
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
|
||||||
|
if (bd->PrevUserCallbackScroll != NULL && window == bd->Window)
|
||||||
|
bd->PrevUserCallbackScroll(window, xoffset, yoffset);
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddMouseWheelEvent((float)xoffset, (float)yoffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode)
|
||||||
|
{
|
||||||
|
#if GLFW_HAS_GET_KEY_NAME && !defined(__EMSCRIPTEN__)
|
||||||
|
// GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult.
|
||||||
|
// (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently)
|
||||||
|
// See https://github.com/glfw/glfw/issues/1502 for details.
|
||||||
|
// Adding a workaround to undo this (so our keys are translated->untranslated->translated, likely a lossy process).
|
||||||
|
// This won't cover edge cases but this is at least going to cover common cases.
|
||||||
|
if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_EQUAL)
|
||||||
|
return key;
|
||||||
|
const char* key_name = glfwGetKeyName(key, scancode);
|
||||||
|
if (key_name && key_name[0] != 0 && key_name[1] == 0)
|
||||||
|
{
|
||||||
|
const char char_names[] = "`-=[]\\,;\'./";
|
||||||
|
const int char_keys[] = { GLFW_KEY_GRAVE_ACCENT, GLFW_KEY_MINUS, GLFW_KEY_EQUAL, GLFW_KEY_LEFT_BRACKET, GLFW_KEY_RIGHT_BRACKET, GLFW_KEY_BACKSLASH, GLFW_KEY_COMMA, GLFW_KEY_SEMICOLON, GLFW_KEY_APOSTROPHE, GLFW_KEY_PERIOD, GLFW_KEY_SLASH, 0 };
|
||||||
|
IM_ASSERT(IM_ARRAYSIZE(char_names) == IM_ARRAYSIZE(char_keys));
|
||||||
|
if (key_name[0] >= '0' && key_name[0] <= '9') { key = GLFW_KEY_0 + (key_name[0] - '0'); }
|
||||||
|
else if (key_name[0] >= 'A' && key_name[0] <= 'Z') { key = GLFW_KEY_A + (key_name[0] - 'A'); }
|
||||||
|
else if (key_name[0] >= 'a' && key_name[0] <= 'z') { key = GLFW_KEY_A + (key_name[0] - 'a'); }
|
||||||
|
else if (const char* p = strchr(char_names, key_name[0])) { key = char_keys[p - char_names]; }
|
||||||
|
}
|
||||||
|
// if (action == GLFW_PRESS) printf("key %d scancode %d name '%s'\n", key, scancode, key_name);
|
||||||
|
#else
|
||||||
|
IM_UNUSED(scancode);
|
||||||
|
#endif
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, int action, int mods)
|
||||||
|
{
|
||||||
|
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
|
||||||
|
if (bd->PrevUserCallbackKey != NULL && window == bd->Window)
|
||||||
|
bd->PrevUserCallbackKey(window, keycode, scancode, action, mods);
|
||||||
|
|
||||||
|
if (action != GLFW_PRESS && action != GLFW_RELEASE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Workaround: X11 does not include current pressed/released modifier key in 'mods' flags. https://github.com/glfw/glfw/issues/1630
|
||||||
|
if (int keycode_to_mod = ImGui_ImplGlfw_KeyToModifier(keycode))
|
||||||
|
mods = (action == GLFW_PRESS) ? (mods | keycode_to_mod) : (mods & ~keycode_to_mod);
|
||||||
|
ImGui_ImplGlfw_UpdateKeyModifiers(mods);
|
||||||
|
|
||||||
|
keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode);
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode);
|
||||||
|
io.AddKeyEvent(imgui_key, (action == GLFW_PRESS));
|
||||||
|
io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code)
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused)
|
||||||
|
{
|
||||||
|
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
|
||||||
|
if (bd->PrevUserCallbackWindowFocus != NULL && window == bd->Window)
|
||||||
|
bd->PrevUserCallbackWindowFocus(window, focused);
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddFocusEvent(focused != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y)
|
||||||
|
{
|
||||||
|
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
|
||||||
|
if (bd->PrevUserCallbackCursorPos != NULL && window == bd->Window)
|
||||||
|
bd->PrevUserCallbackCursorPos(window, x, y);
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddMousePosEvent((float)x, (float)y);
|
||||||
|
bd->LastValidMousePos = ImVec2((float)x, (float)y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround: X11 seems to send spurious Leave/Enter events which would make us lose our position,
|
||||||
|
// so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984)
|
||||||
|
void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered)
|
||||||
|
{
|
||||||
|
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
|
||||||
|
if (bd->PrevUserCallbackCursorEnter != NULL && window == bd->Window)
|
||||||
|
bd->PrevUserCallbackCursorEnter(window, entered);
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if (entered)
|
||||||
|
{
|
||||||
|
bd->MouseWindow = window;
|
||||||
|
io.AddMousePosEvent(bd->LastValidMousePos.x, bd->LastValidMousePos.y);
|
||||||
|
}
|
||||||
|
else if (!entered && bd->MouseWindow == window)
|
||||||
|
{
|
||||||
|
bd->LastValidMousePos = io.MousePos;
|
||||||
|
bd->MouseWindow = NULL;
|
||||||
|
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)
|
||||||
|
{
|
||||||
|
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
|
||||||
|
if (bd->PrevUserCallbackChar != NULL && window == bd->Window)
|
||||||
|
bd->PrevUserCallbackChar(window, c);
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddInputCharacter(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int)
|
||||||
|
{
|
||||||
|
// Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too.
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window)
|
||||||
|
{
|
||||||
|
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
|
||||||
|
IM_ASSERT(bd->InstalledCallbacks == false && "Callbacks already installed!");
|
||||||
|
IM_ASSERT(bd->Window == window);
|
||||||
|
|
||||||
|
bd->PrevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, ImGui_ImplGlfw_WindowFocusCallback);
|
||||||
|
bd->PrevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, ImGui_ImplGlfw_CursorEnterCallback);
|
||||||
|
bd->PrevUserCallbackCursorPos = glfwSetCursorPosCallback(window, ImGui_ImplGlfw_CursorPosCallback);
|
||||||
|
bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback);
|
||||||
|
bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback);
|
||||||
|
bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback);
|
||||||
|
bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback);
|
||||||
|
bd->PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback);
|
||||||
|
bd->InstalledCallbacks = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window)
|
||||||
|
{
|
||||||
|
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
|
||||||
|
IM_ASSERT(bd->InstalledCallbacks == true && "Callbacks not installed!");
|
||||||
|
IM_ASSERT(bd->Window == window);
|
||||||
|
|
||||||
|
glfwSetWindowFocusCallback(window, bd->PrevUserCallbackWindowFocus);
|
||||||
|
glfwSetCursorEnterCallback(window, bd->PrevUserCallbackCursorEnter);
|
||||||
|
glfwSetCursorPosCallback(window, bd->PrevUserCallbackCursorPos);
|
||||||
|
glfwSetMouseButtonCallback(window, bd->PrevUserCallbackMousebutton);
|
||||||
|
glfwSetScrollCallback(window, bd->PrevUserCallbackScroll);
|
||||||
|
glfwSetKeyCallback(window, bd->PrevUserCallbackKey);
|
||||||
|
glfwSetCharCallback(window, bd->PrevUserCallbackChar);
|
||||||
|
glfwSetMonitorCallback(bd->PrevUserCallbackMonitor);
|
||||||
|
bd->InstalledCallbacks = false;
|
||||||
|
bd->PrevUserCallbackWindowFocus = NULL;
|
||||||
|
bd->PrevUserCallbackCursorEnter = NULL;
|
||||||
|
bd->PrevUserCallbackCursorPos = NULL;
|
||||||
|
bd->PrevUserCallbackMousebutton = NULL;
|
||||||
|
bd->PrevUserCallbackScroll = NULL;
|
||||||
|
bd->PrevUserCallbackKey = NULL;
|
||||||
|
bd->PrevUserCallbackChar = NULL;
|
||||||
|
bd->PrevUserCallbackMonitor = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!");
|
||||||
|
|
||||||
|
// Setup backend capabilities flags
|
||||||
|
ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)();
|
||||||
|
io.BackendPlatformUserData = (void*)bd;
|
||||||
|
io.BackendPlatformName = "imgui_impl_glfw";
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
||||||
|
|
||||||
|
bd->Window = window;
|
||||||
|
bd->Time = 0.0;
|
||||||
|
|
||||||
|
io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
|
||||||
|
io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
|
||||||
|
io.ClipboardUserData = bd->Window;
|
||||||
|
|
||||||
|
// Set platform dependent data in viewport
|
||||||
|
#if defined(_WIN32)
|
||||||
|
ImGui::GetMainViewport()->PlatformHandleRaw = (void*)glfwGetWin32Window(bd->Window);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Create mouse cursors
|
||||||
|
// (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist,
|
||||||
|
// GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting.
|
||||||
|
// Missing cursors will return NULL and our _UpdateMouseCursor() function will use the Arrow cursor instead.)
|
||||||
|
GLFWerrorfun prev_error_callback = glfwSetErrorCallback(NULL);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
|
||||||
|
#if GLFW_HAS_NEW_CURSORS
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR);
|
||||||
|
#else
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||||
|
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||||
|
#endif
|
||||||
|
glfwSetErrorCallback(prev_error_callback);
|
||||||
|
|
||||||
|
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
|
||||||
|
if (install_callbacks)
|
||||||
|
ImGui_ImplGlfw_InstallCallbacks(window);
|
||||||
|
|
||||||
|
bd->ClientApi = client_api;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks)
|
||||||
|
{
|
||||||
|
return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks)
|
||||||
|
{
|
||||||
|
return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks)
|
||||||
|
{
|
||||||
|
return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplGlfw_Shutdown()
|
||||||
|
{
|
||||||
|
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
|
||||||
|
IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?");
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
if (bd->InstalledCallbacks)
|
||||||
|
ImGui_ImplGlfw_RestoreCallbacks(bd->Window);
|
||||||
|
|
||||||
|
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
|
||||||
|
glfwDestroyCursor(bd->MouseCursors[cursor_n]);
|
||||||
|
|
||||||
|
io.BackendPlatformName = NULL;
|
||||||
|
io.BackendPlatformUserData = NULL;
|
||||||
|
IM_DELETE(bd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplGlfw_UpdateMouseData()
|
||||||
|
{
|
||||||
|
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
const bool is_app_focused = true;
|
||||||
|
#else
|
||||||
|
const bool is_app_focused = glfwGetWindowAttrib(bd->Window, GLFW_FOCUSED) != 0;
|
||||||
|
#endif
|
||||||
|
if (is_app_focused)
|
||||||
|
{
|
||||||
|
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
|
||||||
|
if (io.WantSetMousePos)
|
||||||
|
glfwSetCursorPos(bd->Window, (double)io.MousePos.x, (double)io.MousePos.y);
|
||||||
|
|
||||||
|
// (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured)
|
||||||
|
if (is_app_focused && bd->MouseWindow == NULL)
|
||||||
|
{
|
||||||
|
double mouse_x, mouse_y;
|
||||||
|
glfwGetCursorPos(bd->Window, &mouse_x, &mouse_y);
|
||||||
|
io.AddMousePosEvent((float)mouse_x, (float)mouse_y);
|
||||||
|
bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplGlfw_UpdateMouseCursor()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
|
||||||
|
if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||||
|
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
|
||||||
|
{
|
||||||
|
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
|
||||||
|
glfwSetInputMode(bd->Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Show OS mouse cursor
|
||||||
|
// FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here.
|
||||||
|
glfwSetCursor(bd->Window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]);
|
||||||
|
glfwSetInputMode(bd->Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update gamepad inputs
|
||||||
|
static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; }
|
||||||
|
static void ImGui_ImplGlfw_UpdateGamepads()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||||
|
#if GLFW_HAS_GAMEPAD_API
|
||||||
|
GLFWgamepadstate gamepad;
|
||||||
|
if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad))
|
||||||
|
return;
|
||||||
|
#define MAP_BUTTON(KEY_NO, BUTTON_NO, _UNUSED) do { io.AddKeyEvent(KEY_NO, gamepad.buttons[BUTTON_NO] != 0); } while (0)
|
||||||
|
#define MAP_ANALOG(KEY_NO, AXIS_NO, _UNUSED, V0, V1) do { float v = gamepad.axes[AXIS_NO]; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0)
|
||||||
|
#else
|
||||||
|
int axes_count = 0, buttons_count = 0;
|
||||||
|
const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count);
|
||||||
|
const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count);
|
||||||
|
if (axes_count == 0 || buttons_count == 0)
|
||||||
|
return;
|
||||||
|
#define MAP_BUTTON(KEY_NO, _UNUSED, BUTTON_NO) do { io.AddKeyEvent(KEY_NO, (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS)); } while (0)
|
||||||
|
#define MAP_ANALOG(KEY_NO, _UNUSED, AXIS_NO, V0, V1) do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0)
|
||||||
|
#endif
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadStart, GLFW_GAMEPAD_BUTTON_START, 7);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadBack, GLFW_GAMEPAD_BUTTON_BACK, 6);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceDown, GLFW_GAMEPAD_BUTTON_A, 0); // Xbox A, PS Cross
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceRight, GLFW_GAMEPAD_BUTTON_B, 1); // Xbox B, PS Circle
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceLeft, GLFW_GAMEPAD_BUTTON_X, 2); // Xbox X, PS Square
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadFaceUp, GLFW_GAMEPAD_BUTTON_Y, 3); // Xbox Y, PS Triangle
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadLeft, GLFW_GAMEPAD_BUTTON_DPAD_LEFT, 13);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadRight, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, 11);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadUp, GLFW_GAMEPAD_BUTTON_DPAD_UP, 10);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadDpadDown, GLFW_GAMEPAD_BUTTON_DPAD_DOWN, 12);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadL1, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, 4);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadR1, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, 5);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadL2, GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, 4, -0.75f, +1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadR2, GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, 5, -0.75f, +1.0f);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadL3, GLFW_GAMEPAD_BUTTON_LEFT_THUMB, 8);
|
||||||
|
MAP_BUTTON(ImGuiKey_GamepadR3, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, 9);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickLeft, GLFW_GAMEPAD_AXIS_LEFT_X, 0, -0.25f, -1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickRight, GLFW_GAMEPAD_AXIS_LEFT_X, 0, +0.25f, +1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickUp, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, -0.25f, -1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadLStickDown, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, +0.25f, +1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickLeft, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, -0.25f, -1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickRight, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, +0.25f, +1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickUp, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, -0.25f, -1.0f);
|
||||||
|
MAP_ANALOG(ImGuiKey_GamepadRStickDown, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, +0.25f, +1.0f);
|
||||||
|
#undef MAP_BUTTON
|
||||||
|
#undef MAP_ANALOG
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplGlfw_NewFrame()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
|
||||||
|
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplGlfw_InitForXXX()?");
|
||||||
|
|
||||||
|
// Setup display size (every frame to accommodate for window resizing)
|
||||||
|
int w, h;
|
||||||
|
int display_w, display_h;
|
||||||
|
glfwGetWindowSize(bd->Window, &w, &h);
|
||||||
|
glfwGetFramebufferSize(bd->Window, &display_w, &display_h);
|
||||||
|
io.DisplaySize = ImVec2((float)w, (float)h);
|
||||||
|
if (w > 0 && h > 0)
|
||||||
|
io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h);
|
||||||
|
|
||||||
|
// Setup time step
|
||||||
|
double current_time = glfwGetTime();
|
||||||
|
io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f);
|
||||||
|
bd->Time = current_time;
|
||||||
|
|
||||||
|
ImGui_ImplGlfw_UpdateMouseData();
|
||||||
|
ImGui_ImplGlfw_UpdateMouseCursor();
|
||||||
|
|
||||||
|
// Update game controllers (if enabled and available)
|
||||||
|
ImGui_ImplGlfw_UpdateGamepads();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
46
libs/imgui/backends/imgui_impl_glfw.h
Normal file
46
libs/imgui/backends/imgui_impl_glfw.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// dear imgui: Platform Backend for GLFW
|
||||||
|
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..)
|
||||||
|
// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Platform: Clipboard support.
|
||||||
|
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||||
|
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||||
|
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||||
|
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||||
|
|
||||||
|
// About GLSL version:
|
||||||
|
// The 'glsl_version' initialization parameter defaults to "#version 150" if NULL.
|
||||||
|
// Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure!
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
|
|
||||||
|
struct GLFWwindow;
|
||||||
|
struct GLFWmonitor;
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks);
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks);
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
|
||||||
|
|
||||||
|
// GLFW callbacks (installer)
|
||||||
|
// - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any.
|
||||||
|
// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks.
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window);
|
||||||
|
|
||||||
|
// GLFW callbacks (individual callbacks to call if you didn't install callbacks)
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event);
|
||||||
866
libs/imgui/backends/imgui_impl_opengl3.cpp
Normal file
866
libs/imgui/backends/imgui_impl_opengl3.cpp
Normal file
@@ -0,0 +1,866 @@
|
|||||||
|
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
|
||||||
|
// - Desktop GL: 2.x 3.x 4.x
|
||||||
|
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
|
||||||
|
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
|
||||||
|
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||||
|
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2022-05-23: OpenGL: Reworking 2021-12-15 "Using buffer orphaning" so it only happens on Intel GPU, seems to cause problems otherwise. (#4468, #4825, #4832, #5127).
|
||||||
|
// 2022-05-13: OpenGL: Fix state corruption on OpenGL ES 2.0 due to not preserving GL_ELEMENT_ARRAY_BUFFER_BINDING and vertex attribute states.
|
||||||
|
// 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers.
|
||||||
|
// 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions.
|
||||||
|
// 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader.
|
||||||
|
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||||
|
// 2021-06-25: OpenGL: Use OES_vertex_array extension on Emscripten + backup/restore current state.
|
||||||
|
// 2021-06-21: OpenGL: Destroy individual vertex/fragment shader objects right after they are linked into the main shader.
|
||||||
|
// 2021-05-24: OpenGL: Access GL_CLIP_ORIGIN when "GL_ARB_clip_control" extension is detected, inside of just OpenGL 4.5 version.
|
||||||
|
// 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||||
|
// 2021-04-06: OpenGL: Don't try to read GL_CLIP_ORIGIN unless we're OpenGL 4.5 or greater.
|
||||||
|
// 2021-02-18: OpenGL: Change blending equation to preserve alpha in output buffer.
|
||||||
|
// 2021-01-03: OpenGL: Backup, setup and restore GL_STENCIL_TEST state.
|
||||||
|
// 2020-10-23: OpenGL: Backup, setup and restore GL_PRIMITIVE_RESTART state.
|
||||||
|
// 2020-10-15: OpenGL: Use glGetString(GL_VERSION) instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x)
|
||||||
|
// 2020-09-17: OpenGL: Fix to avoid compiling/calling glBindSampler() on ES or pre 3.3 context which have the defines set by a loader.
|
||||||
|
// 2020-07-10: OpenGL: Added support for glad2 OpenGL loader.
|
||||||
|
// 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX.
|
||||||
|
// 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix.
|
||||||
|
// 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset.
|
||||||
|
// 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader.
|
||||||
|
// 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader.
|
||||||
|
// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders.
|
||||||
|
// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility.
|
||||||
|
// 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call.
|
||||||
|
// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||||
|
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||||
|
// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.
|
||||||
|
// 2019-03-15: OpenGL: Added a GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.
|
||||||
|
// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).
|
||||||
|
// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.
|
||||||
|
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
|
||||||
|
// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
|
||||||
|
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||||
|
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.
|
||||||
|
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
|
||||||
|
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
|
||||||
|
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
|
||||||
|
// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
|
||||||
|
// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
|
||||||
|
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
|
||||||
|
// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
|
||||||
|
// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
|
||||||
|
// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
|
||||||
|
// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
|
||||||
|
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
|
||||||
|
// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
|
||||||
|
// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
|
||||||
|
// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
|
||||||
|
// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
|
||||||
|
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
|
||||||
|
// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
|
||||||
|
|
||||||
|
//----------------------------------------
|
||||||
|
// OpenGL GLSL GLSL
|
||||||
|
// version version string
|
||||||
|
//----------------------------------------
|
||||||
|
// 2.0 110 "#version 110"
|
||||||
|
// 2.1 120 "#version 120"
|
||||||
|
// 3.0 130 "#version 130"
|
||||||
|
// 3.1 140 "#version 140"
|
||||||
|
// 3.2 150 "#version 150"
|
||||||
|
// 3.3 330 "#version 330 core"
|
||||||
|
// 4.0 400 "#version 400 core"
|
||||||
|
// 4.1 410 "#version 410 core"
|
||||||
|
// 4.2 420 "#version 410 core"
|
||||||
|
// 4.3 430 "#version 430 core"
|
||||||
|
// ES 2.0 100 "#version 100" = WebGL 1.0
|
||||||
|
// ES 3.0 300 "#version 300 es" = WebGL 2.0
|
||||||
|
//----------------------------------------
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_impl_opengl3.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
|
||||||
|
#include <stddef.h> // intptr_t
|
||||||
|
#else
|
||||||
|
#include <stdint.h> // intptr_t
|
||||||
|
#endif
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Clang warnings with -Weverything
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
|
||||||
|
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||||
|
#if __has_warning("-Wzero-as-null-pointer-constant")
|
||||||
|
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// GL includes
|
||||||
|
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||||
|
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
|
||||||
|
#include <OpenGLES/ES2/gl.h> // Use GL ES 2
|
||||||
|
#else
|
||||||
|
#include <GLES2/gl2.h> // Use GL ES 2
|
||||||
|
#endif
|
||||||
|
#if defined(__EMSCRIPTEN__)
|
||||||
|
#ifndef GL_GLEXT_PROTOTYPES
|
||||||
|
#define GL_GLEXT_PROTOTYPES
|
||||||
|
#endif
|
||||||
|
#include <GLES2/gl2ext.h>
|
||||||
|
#endif
|
||||||
|
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||||
|
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
|
||||||
|
#include <OpenGLES/ES3/gl.h> // Use GL ES 3
|
||||||
|
#else
|
||||||
|
#include <GLES3/gl3.h> // Use GL ES 3
|
||||||
|
#endif
|
||||||
|
#elif !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
|
||||||
|
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
|
||||||
|
// Helper libraries are often used for this purpose! Here we are using our own minimal custom loader based on gl3w.
|
||||||
|
// In the rest of your app/engine, you can use another loader of your choice (gl3w, glew, glad, glbinding, glext, glLoadGen, etc.).
|
||||||
|
// If you happen to be developing a new feature for this backend (imgui_impl_opengl3.cpp):
|
||||||
|
// - You may need to regenerate imgui_impl_opengl3_loader.h to add new symbols. See https://github.com/dearimgui/gl3w_stripped
|
||||||
|
// - You can temporarily use an unstripped version. See https://github.com/dearimgui/gl3w_stripped/releases
|
||||||
|
// Changes to this backend using new APIs should be accompanied by a regenerated stripped loader version.
|
||||||
|
#define IMGL3W_IMPL
|
||||||
|
#include <GL/glew.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Vertex arrays are not supported on ES2/WebGL1 unless Emscripten which uses an extension
|
||||||
|
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||||
|
#define IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
|
#elif defined(__EMSCRIPTEN__)
|
||||||
|
#define IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
|
#define glBindVertexArray glBindVertexArrayOES
|
||||||
|
#define glGenVertexArrays glGenVertexArraysOES
|
||||||
|
#define glDeleteVertexArrays glDeleteVertexArraysOES
|
||||||
|
#define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_OES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Desktop GL 2.0+ has glPolygonMode() which GL ES and WebGL don't have.
|
||||||
|
#ifdef GL_POLYGON_MODE
|
||||||
|
#define IMGUI_IMPL_HAS_POLYGON_MODE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
|
||||||
|
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_2)
|
||||||
|
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Desktop GL 3.3+ has glBindSampler()
|
||||||
|
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_3)
|
||||||
|
#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state
|
||||||
|
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1)
|
||||||
|
#define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Desktop GL use extension detection
|
||||||
|
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
|
||||||
|
#define IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// OpenGL Data
|
||||||
|
struct ImGui_ImplOpenGL3_Data
|
||||||
|
{
|
||||||
|
GLuint GlVersion; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
|
||||||
|
char GlslVersionString[32]; // Specified by user or detected based on compile time GL settings.
|
||||||
|
GLuint FontTexture;
|
||||||
|
GLuint ShaderHandle;
|
||||||
|
GLint AttribLocationTex; // Uniforms location
|
||||||
|
GLint AttribLocationProjMtx;
|
||||||
|
GLuint AttribLocationVtxPos; // Vertex attributes location
|
||||||
|
GLuint AttribLocationVtxUV;
|
||||||
|
GLuint AttribLocationVtxColor;
|
||||||
|
unsigned int VboHandle, ElementsHandle;
|
||||||
|
GLsizeiptr VertexBufferSize;
|
||||||
|
GLsizeiptr IndexBufferSize;
|
||||||
|
bool HasClipOrigin;
|
||||||
|
bool UseBufferSubData;
|
||||||
|
|
||||||
|
ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||||
|
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
|
||||||
|
static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData()
|
||||||
|
{
|
||||||
|
return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenGL vertex attribute state (for ES 1.0 and ES 2.0 only)
|
||||||
|
#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
|
struct ImGui_ImplOpenGL3_VtxAttribState
|
||||||
|
{
|
||||||
|
GLint Enabled, Size, Type, Normalized, Stride;
|
||||||
|
GLvoid* Ptr;
|
||||||
|
|
||||||
|
void GetState(GLint index)
|
||||||
|
{
|
||||||
|
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &Enabled);
|
||||||
|
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_SIZE, &Size);
|
||||||
|
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_TYPE, &Type);
|
||||||
|
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &Normalized);
|
||||||
|
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &Stride);
|
||||||
|
glGetVertexAttribPointerv(index, GL_VERTEX_ATTRIB_ARRAY_POINTER, &Ptr);
|
||||||
|
}
|
||||||
|
void SetState(GLint index)
|
||||||
|
{
|
||||||
|
glVertexAttribPointer(index, Size, Type, (GLboolean)Normalized, Stride, Ptr);
|
||||||
|
if (Enabled) glEnableVertexAttribArray(index); else glDisableVertexAttribArray(index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize our loader
|
||||||
|
glewInit();
|
||||||
|
|
||||||
|
// Setup backend capabilities flags
|
||||||
|
ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)();
|
||||||
|
io.BackendRendererUserData = (void*)bd;
|
||||||
|
io.BackendRendererName = "imgui_impl_opengl3";
|
||||||
|
|
||||||
|
// Query for GL version (e.g. 320 for GL 3.2)
|
||||||
|
#if !defined(IMGUI_IMPL_OPENGL_ES2)
|
||||||
|
GLint major = 0;
|
||||||
|
GLint minor = 0;
|
||||||
|
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
||||||
|
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
||||||
|
if (major == 0 && minor == 0)
|
||||||
|
{
|
||||||
|
// Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
|
||||||
|
const char* gl_version = (const char*)glGetString(GL_VERSION);
|
||||||
|
sscanf(gl_version, "%d.%d", &major, &minor);
|
||||||
|
}
|
||||||
|
bd->GlVersion = (GLuint)(major * 100 + minor * 10);
|
||||||
|
|
||||||
|
// Query vendor to enable glBufferSubData kludge
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (const char* vendor = (const char*)glGetString(GL_VENDOR))
|
||||||
|
if (strncmp(vendor, "Intel", 5) == 0)
|
||||||
|
bd->UseBufferSubData = true;
|
||||||
|
#endif
|
||||||
|
//printf("GL_MAJOR_VERSION = %d\nGL_MINOR_VERSION = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", major, minor, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG]
|
||||||
|
#else
|
||||||
|
bd->GlVersion = 200; // GLES 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
|
||||||
|
if (bd->GlVersion >= 320)
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Store GLSL version string so we can refer to it later in case we recreate shaders.
|
||||||
|
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
|
||||||
|
if (glsl_version == NULL)
|
||||||
|
{
|
||||||
|
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||||
|
glsl_version = "#version 100";
|
||||||
|
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||||
|
glsl_version = "#version 300 es";
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
glsl_version = "#version 150";
|
||||||
|
#else
|
||||||
|
glsl_version = "#version 130";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(bd->GlslVersionString));
|
||||||
|
strcpy(bd->GlslVersionString, glsl_version);
|
||||||
|
strcat(bd->GlslVersionString, "\n");
|
||||||
|
|
||||||
|
// Make an arbitrary GL call (we don't actually need the result)
|
||||||
|
// IF YOU GET A CRASH HERE: it probably means the OpenGL function loader didn't do its job. Let us know!
|
||||||
|
GLint current_texture;
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture);
|
||||||
|
|
||||||
|
// Detect extensions we support
|
||||||
|
bd->HasClipOrigin = (bd->GlVersion >= 450);
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS
|
||||||
|
GLint num_extensions = 0;
|
||||||
|
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
|
||||||
|
for (GLint i = 0; i < num_extensions; i++)
|
||||||
|
{
|
||||||
|
const char* extension = (const char*)glGetStringi(GL_EXTENSIONS, i);
|
||||||
|
if (extension != NULL && strcmp(extension, "GL_ARB_clip_control") == 0)
|
||||||
|
bd->HasClipOrigin = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplOpenGL3_Shutdown()
|
||||||
|
{
|
||||||
|
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||||
|
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
||||||
|
io.BackendRendererName = NULL;
|
||||||
|
io.BackendRendererUserData = NULL;
|
||||||
|
IM_DELETE(bd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplOpenGL3_NewFrame()
|
||||||
|
{
|
||||||
|
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||||
|
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplOpenGL3_Init()?");
|
||||||
|
|
||||||
|
if (!bd->ShaderHandle)
|
||||||
|
ImGui_ImplOpenGL3_CreateDeviceObjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
|
||||||
|
{
|
||||||
|
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||||
|
|
||||||
|
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendEquation(GL_FUNC_ADD);
|
||||||
|
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
glDisable(GL_STENCIL_TEST);
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
|
||||||
|
if (bd->GlVersion >= 310)
|
||||||
|
glDisable(GL_PRIMITIVE_RESTART);
|
||||||
|
#endif
|
||||||
|
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
|
||||||
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
|
||||||
|
#if defined(GL_CLIP_ORIGIN)
|
||||||
|
bool clip_origin_lower_left = true;
|
||||||
|
if (bd->HasClipOrigin)
|
||||||
|
{
|
||||||
|
GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)¤t_clip_origin);
|
||||||
|
if (current_clip_origin == GL_UPPER_LEFT)
|
||||||
|
clip_origin_lower_left = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Setup viewport, orthographic projection matrix
|
||||||
|
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||||
|
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
|
||||||
|
float L = draw_data->DisplayPos.x;
|
||||||
|
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||||
|
float T = draw_data->DisplayPos.y;
|
||||||
|
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||||
|
#if defined(GL_CLIP_ORIGIN)
|
||||||
|
if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left
|
||||||
|
#endif
|
||||||
|
const float ortho_projection[4][4] =
|
||||||
|
{
|
||||||
|
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||||
|
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||||
|
{ 0.0f, 0.0f, -1.0f, 0.0f },
|
||||||
|
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
|
||||||
|
};
|
||||||
|
glUseProgram(bd->ShaderHandle);
|
||||||
|
glUniform1i(bd->AttribLocationTex, 0);
|
||||||
|
glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
|
||||||
|
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||||
|
if (bd->GlVersion >= 330)
|
||||||
|
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
(void)vertex_array_object;
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
|
glBindVertexArray(vertex_array_object);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Bind vertex/index buffers and setup attributes for ImDrawVert
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle);
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle);
|
||||||
|
glEnableVertexAttribArray(bd->AttribLocationVtxPos);
|
||||||
|
glEnableVertexAttribArray(bd->AttribLocationVtxUV);
|
||||||
|
glEnableVertexAttribArray(bd->AttribLocationVtxColor);
|
||||||
|
glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
|
||||||
|
glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
|
||||||
|
glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenGL3 Render function.
|
||||||
|
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly.
|
||||||
|
// This is in order to be able to run within an OpenGL engine that doesn't do so.
|
||||||
|
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||||
|
{
|
||||||
|
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
||||||
|
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
|
||||||
|
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
|
||||||
|
if (fb_width <= 0 || fb_height <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||||
|
|
||||||
|
// Backup GL state
|
||||||
|
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program);
|
||||||
|
GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture);
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||||
|
GLuint last_sampler; if (bd->GlVersion >= 330) { glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler); } else { last_sampler = 0; }
|
||||||
|
#endif
|
||||||
|
GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer);
|
||||||
|
#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
|
// This is part of VAO on OpenGL 3.0+ and OpenGL ES 3.0+.
|
||||||
|
GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer);
|
||||||
|
ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_pos; last_vtx_attrib_state_pos.GetState(bd->AttribLocationVtxPos);
|
||||||
|
ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_uv; last_vtx_attrib_state_uv.GetState(bd->AttribLocationVtxUV);
|
||||||
|
ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_color; last_vtx_attrib_state_color.GetState(bd->AttribLocationVtxColor);
|
||||||
|
#endif
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
|
GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
|
||||||
|
#endif
|
||||||
|
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
|
||||||
|
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
|
||||||
|
#endif
|
||||||
|
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_stencil_test = glIsEnabled(GL_STENCIL_TEST);
|
||||||
|
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
|
||||||
|
GLboolean last_enable_primitive_restart = (bd->GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Setup desired GL state
|
||||||
|
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
|
||||||
|
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
|
||||||
|
GLuint vertex_array_object = 0;
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
|
glGenVertexArrays(1, &vertex_array_object);
|
||||||
|
#endif
|
||||||
|
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
|
||||||
|
|
||||||
|
// Will project scissor/clipping rectangles into framebuffer space
|
||||||
|
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
|
||||||
|
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
|
||||||
|
|
||||||
|
// Render command lists
|
||||||
|
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||||
|
{
|
||||||
|
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||||
|
|
||||||
|
// Upload vertex/index buffers
|
||||||
|
// - On Intel windows drivers we got reports that regular glBufferData() led to accumulating leaks when using multi-viewports, so we started using orphaning + glBufferSubData(). (See https://github.com/ocornut/imgui/issues/4468)
|
||||||
|
// - On NVIDIA drivers we got reports that using orphaning + glBufferSubData() led to glitches when using multi-viewports.
|
||||||
|
// - OpenGL drivers are in a very sorry state in 2022, for now we are switching code path based on vendors.
|
||||||
|
const GLsizeiptr vtx_buffer_size = (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert);
|
||||||
|
const GLsizeiptr idx_buffer_size = (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx);
|
||||||
|
if (bd->UseBufferSubData)
|
||||||
|
{
|
||||||
|
if (bd->VertexBufferSize < vtx_buffer_size)
|
||||||
|
{
|
||||||
|
bd->VertexBufferSize = vtx_buffer_size;
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, NULL, GL_STREAM_DRAW);
|
||||||
|
}
|
||||||
|
if (bd->IndexBufferSize < idx_buffer_size)
|
||||||
|
{
|
||||||
|
bd->IndexBufferSize = idx_buffer_size;
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, NULL, GL_STREAM_DRAW);
|
||||||
|
}
|
||||||
|
glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data);
|
||||||
|
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, (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 != NULL)
|
||||||
|
{
|
||||||
|
// User callback, registered via ImDrawList::AddCallback()
|
||||||
|
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||||
|
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||||
|
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
|
||||||
|
else
|
||||||
|
pcmd->UserCallback(cmd_list, pcmd);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Project scissor/clipping rectangles into framebuffer space
|
||||||
|
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
|
||||||
|
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
|
||||||
|
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Apply scissor/clipping rectangle (Y is inverted in OpenGL)
|
||||||
|
glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y));
|
||||||
|
|
||||||
|
// Bind texture, Draw
|
||||||
|
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID());
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
|
||||||
|
if (bd->GlVersion >= 320)
|
||||||
|
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy the temporary VAO
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
|
glDeleteVertexArrays(1, &vertex_array_object);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Restore modified GL state
|
||||||
|
glUseProgram(last_program);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||||
|
if (bd->GlVersion >= 330)
|
||||||
|
glBindSampler(0, last_sampler);
|
||||||
|
#endif
|
||||||
|
glActiveTexture(last_active_texture);
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
|
glBindVertexArray(last_vertex_array_object);
|
||||||
|
#endif
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
|
||||||
|
#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer);
|
||||||
|
last_vtx_attrib_state_pos.SetState(bd->AttribLocationVtxPos);
|
||||||
|
last_vtx_attrib_state_uv.SetState(bd->AttribLocationVtxUV);
|
||||||
|
last_vtx_attrib_state_color.SetState(bd->AttribLocationVtxColor);
|
||||||
|
#endif
|
||||||
|
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_stencil_test) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST);
|
||||||
|
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
|
||||||
|
if (bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
|
||||||
|
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
|
||||||
|
#endif
|
||||||
|
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]);
|
||||||
|
(void)bd; // Not all compilation paths use this
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplOpenGL3_CreateFontsTexture()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||||
|
|
||||||
|
// Build texture atlas
|
||||||
|
unsigned char* pixels;
|
||||||
|
int width, height;
|
||||||
|
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (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.
|
||||||
|
|
||||||
|
// Upload texture to graphics system
|
||||||
|
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||||
|
GLint last_texture;
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||||
|
glGenTextures(1, &bd->FontTexture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, bd->FontTexture);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
#ifdef GL_UNPACK_ROW_LENGTH // Not on WebGL/ES
|
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||||
|
#endif
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||||
|
|
||||||
|
// Store our identifier
|
||||||
|
io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
|
||||||
|
|
||||||
|
// Restore state
|
||||||
|
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplOpenGL3_DestroyFontsTexture()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||||
|
if (bd->FontTexture)
|
||||||
|
{
|
||||||
|
glDeleteTextures(1, &bd->FontTexture);
|
||||||
|
io.Fonts->SetTexID(0);
|
||||||
|
bd->FontTexture = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
|
||||||
|
static bool CheckShader(GLuint handle, const char* desc)
|
||||||
|
{
|
||||||
|
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||||
|
GLint status = 0, log_length = 0;
|
||||||
|
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
|
||||||
|
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
|
||||||
|
if ((GLboolean)status == GL_FALSE)
|
||||||
|
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s! With GLSL: %s\n", desc, bd->GlslVersionString);
|
||||||
|
if (log_length > 1)
|
||||||
|
{
|
||||||
|
ImVector<char> buf;
|
||||||
|
buf.resize((int)(log_length + 1));
|
||||||
|
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
|
||||||
|
fprintf(stderr, "%s\n", buf.begin());
|
||||||
|
}
|
||||||
|
return (GLboolean)status == GL_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
|
||||||
|
static bool CheckProgram(GLuint handle, const char* desc)
|
||||||
|
{
|
||||||
|
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||||
|
GLint status = 0, log_length = 0;
|
||||||
|
glGetProgramiv(handle, GL_LINK_STATUS, &status);
|
||||||
|
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
|
||||||
|
if ((GLboolean)status == GL_FALSE)
|
||||||
|
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! With GLSL %s\n", desc, bd->GlslVersionString);
|
||||||
|
if (log_length > 1)
|
||||||
|
{
|
||||||
|
ImVector<char> buf;
|
||||||
|
buf.resize((int)(log_length + 1));
|
||||||
|
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
|
||||||
|
fprintf(stderr, "%s\n", buf.begin());
|
||||||
|
}
|
||||||
|
return (GLboolean)status == GL_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
|
||||||
|
{
|
||||||
|
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||||
|
|
||||||
|
// Backup GL state
|
||||||
|
GLint last_texture, last_array_buffer;
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||||
|
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
|
GLint last_vertex_array;
|
||||||
|
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Parse GLSL version string
|
||||||
|
int glsl_version = 130;
|
||||||
|
sscanf(bd->GlslVersionString, "#version %d", &glsl_version);
|
||||||
|
|
||||||
|
const GLchar* vertex_shader_glsl_120 =
|
||||||
|
"uniform mat4 ProjMtx;\n"
|
||||||
|
"attribute vec2 Position;\n"
|
||||||
|
"attribute vec2 UV;\n"
|
||||||
|
"attribute vec4 Color;\n"
|
||||||
|
"varying vec2 Frag_UV;\n"
|
||||||
|
"varying 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* vertex_shader_glsl_130 =
|
||||||
|
"uniform mat4 ProjMtx;\n"
|
||||||
|
"in vec2 Position;\n"
|
||||||
|
"in vec2 UV;\n"
|
||||||
|
"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* vertex_shader_glsl_300_es =
|
||||||
|
"precision highp float;\n"
|
||||||
|
"layout (location = 0) in vec2 Position;\n"
|
||||||
|
"layout (location = 1) in vec2 UV;\n"
|
||||||
|
"layout (location = 2) in vec4 Color;\n"
|
||||||
|
"uniform mat4 ProjMtx;\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* vertex_shader_glsl_410_core =
|
||||||
|
"layout (location = 0) in vec2 Position;\n"
|
||||||
|
"layout (location = 1) in vec2 UV;\n"
|
||||||
|
"layout (location = 2) in vec4 Color;\n"
|
||||||
|
"uniform mat4 ProjMtx;\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_glsl_120 =
|
||||||
|
"#ifdef GL_ES\n"
|
||||||
|
" precision mediump float;\n"
|
||||||
|
"#endif\n"
|
||||||
|
"uniform sampler2D Texture;\n"
|
||||||
|
"varying vec2 Frag_UV;\n"
|
||||||
|
"varying vec4 Frag_Color;\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n"
|
||||||
|
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
const GLchar* fragment_shader_glsl_130 =
|
||||||
|
"uniform sampler2D Texture;\n"
|
||||||
|
"in vec2 Frag_UV;\n"
|
||||||
|
"in vec4 Frag_Color;\n"
|
||||||
|
"out vec4 Out_Color;\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n"
|
||||||
|
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
const GLchar* fragment_shader_glsl_300_es =
|
||||||
|
"precision mediump float;\n"
|
||||||
|
"uniform sampler2D Texture;\n"
|
||||||
|
"in vec2 Frag_UV;\n"
|
||||||
|
"in vec4 Frag_Color;\n"
|
||||||
|
"layout (location = 0) out vec4 Out_Color;\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n"
|
||||||
|
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
const GLchar* fragment_shader_glsl_410_core =
|
||||||
|
"in vec2 Frag_UV;\n"
|
||||||
|
"in vec4 Frag_Color;\n"
|
||||||
|
"uniform sampler2D Texture;\n"
|
||||||
|
"layout (location = 0) out vec4 Out_Color;\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n"
|
||||||
|
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
// Select shaders matching our GLSL versions
|
||||||
|
const GLchar* vertex_shader = NULL;
|
||||||
|
const GLchar* fragment_shader = NULL;
|
||||||
|
if (glsl_version < 130)
|
||||||
|
{
|
||||||
|
vertex_shader = vertex_shader_glsl_120;
|
||||||
|
fragment_shader = fragment_shader_glsl_120;
|
||||||
|
}
|
||||||
|
else if (glsl_version >= 410)
|
||||||
|
{
|
||||||
|
vertex_shader = vertex_shader_glsl_410_core;
|
||||||
|
fragment_shader = fragment_shader_glsl_410_core;
|
||||||
|
}
|
||||||
|
else if (glsl_version == 300)
|
||||||
|
{
|
||||||
|
vertex_shader = vertex_shader_glsl_300_es;
|
||||||
|
fragment_shader = fragment_shader_glsl_300_es;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vertex_shader = vertex_shader_glsl_130;
|
||||||
|
fragment_shader = fragment_shader_glsl_130;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create shaders
|
||||||
|
const GLchar* vertex_shader_with_version[2] = { bd->GlslVersionString, vertex_shader };
|
||||||
|
GLuint vert_handle = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
glShaderSource(vert_handle, 2, vertex_shader_with_version, NULL);
|
||||||
|
glCompileShader(vert_handle);
|
||||||
|
CheckShader(vert_handle, "vertex shader");
|
||||||
|
|
||||||
|
const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader };
|
||||||
|
GLuint frag_handle = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
glShaderSource(frag_handle, 2, fragment_shader_with_version, NULL);
|
||||||
|
glCompileShader(frag_handle);
|
||||||
|
CheckShader(frag_handle, "fragment shader");
|
||||||
|
|
||||||
|
// Link
|
||||||
|
bd->ShaderHandle = glCreateProgram();
|
||||||
|
glAttachShader(bd->ShaderHandle, vert_handle);
|
||||||
|
glAttachShader(bd->ShaderHandle, frag_handle);
|
||||||
|
glLinkProgram(bd->ShaderHandle);
|
||||||
|
CheckProgram(bd->ShaderHandle, "shader program");
|
||||||
|
|
||||||
|
glDetachShader(bd->ShaderHandle, vert_handle);
|
||||||
|
glDetachShader(bd->ShaderHandle, frag_handle);
|
||||||
|
glDeleteShader(vert_handle);
|
||||||
|
glDeleteShader(frag_handle);
|
||||||
|
|
||||||
|
bd->AttribLocationTex = glGetUniformLocation(bd->ShaderHandle, "Texture");
|
||||||
|
bd->AttribLocationProjMtx = glGetUniformLocation(bd->ShaderHandle, "ProjMtx");
|
||||||
|
bd->AttribLocationVtxPos = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Position");
|
||||||
|
bd->AttribLocationVtxUV = (GLuint)glGetAttribLocation(bd->ShaderHandle, "UV");
|
||||||
|
bd->AttribLocationVtxColor = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Color");
|
||||||
|
|
||||||
|
// Create buffers
|
||||||
|
glGenBuffers(1, &bd->VboHandle);
|
||||||
|
glGenBuffers(1, &bd->ElementsHandle);
|
||||||
|
|
||||||
|
ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||||
|
|
||||||
|
// Restore modified GL state
|
||||||
|
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
|
||||||
|
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||||
|
glBindVertexArray(last_vertex_array);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
|
||||||
|
{
|
||||||
|
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||||
|
if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; }
|
||||||
|
if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; }
|
||||||
|
if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; }
|
||||||
|
ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
55
libs/imgui/backends/imgui_impl_opengl3.h
Normal file
55
libs/imgui/backends/imgui_impl_opengl3.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
|
||||||
|
// - Desktop GL: 2.x 3.x 4.x
|
||||||
|
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
|
||||||
|
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
||||||
|
|
||||||
|
// Implemented features:
|
||||||
|
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
|
||||||
|
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
|
||||||
|
|
||||||
|
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||||
|
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||||
|
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||||
|
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||||
|
|
||||||
|
// About GLSL version:
|
||||||
|
// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string.
|
||||||
|
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
|
||||||
|
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
|
|
||||||
|
// Backend API
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
|
||||||
|
|
||||||
|
// (Optional) Called by Init/NewFrame/Shutdown
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
||||||
|
|
||||||
|
// Specific OpenGL ES versions
|
||||||
|
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
|
||||||
|
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
|
||||||
|
|
||||||
|
// You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
|
||||||
|
#if !defined(IMGUI_IMPL_OPENGL_ES2) \
|
||||||
|
&& !defined(IMGUI_IMPL_OPENGL_ES3)
|
||||||
|
|
||||||
|
// Try to detect GLES on matching platforms
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#endif
|
||||||
|
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
|
||||||
|
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
|
||||||
|
#elif defined(__EMSCRIPTEN__) || defined(__amigaos4__)
|
||||||
|
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
|
||||||
|
#else
|
||||||
|
// Otherwise imgui_impl_opengl3_loader.h will be used.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
125
libs/imgui/imconfig.h
Normal file
125
libs/imgui/imconfig.h
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
|
||||||
|
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||||
|
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
|
||||||
|
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
|
||||||
|
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
||||||
|
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||||
|
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//---- Define assertion handler. Defaults to calling assert().
|
||||||
|
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||||
|
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||||
|
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||||
|
|
||||||
|
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||||
|
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||||
|
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||||
|
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||||
|
//#define IMGUI_API __declspec( dllexport )
|
||||||
|
//#define IMGUI_API __declspec( dllimport )
|
||||||
|
|
||||||
|
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
|
||||||
|
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
|
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions.
|
||||||
|
|
||||||
|
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||||
|
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||||
|
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||||
|
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
||||||
|
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowStackToolWindow() will be empty (this was called IMGUI_DISABLE_METRICS_WINDOW before 1.88).
|
||||||
|
|
||||||
|
//---- Don't implement some functions to reduce linkage requirements.
|
||||||
|
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
|
||||||
|
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
|
||||||
|
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||||
|
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
|
||||||
|
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||||
|
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||||
|
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||||
|
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||||
|
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||||
|
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||||
|
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||||
|
|
||||||
|
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||||
|
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||||
|
|
||||||
|
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||||
|
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||||
|
|
||||||
|
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||||
|
//#define IMGUI_USE_WCHAR32
|
||||||
|
|
||||||
|
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||||
|
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
|
||||||
|
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||||
|
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||||
|
//#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if enabled
|
||||||
|
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||||
|
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
|
||||||
|
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
|
||||||
|
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
|
||||||
|
//#define IMGUI_USE_STB_SPRINTF
|
||||||
|
|
||||||
|
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
||||||
|
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
||||||
|
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||||
|
//#define IMGUI_ENABLE_FREETYPE
|
||||||
|
|
||||||
|
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||||
|
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
|
||||||
|
//#define IMGUI_ENABLE_STB_TRUETYPE
|
||||||
|
|
||||||
|
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
|
||||||
|
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
|
||||||
|
/*
|
||||||
|
#define IM_VEC2_CLASS_EXTRA \
|
||||||
|
constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \
|
||||||
|
operator MyVec2() const { return MyVec2(x,y); }
|
||||||
|
|
||||||
|
#define IM_VEC4_CLASS_EXTRA \
|
||||||
|
constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \
|
||||||
|
operator MyVec4() const { return MyVec4(x,y,z,w); }
|
||||||
|
*/
|
||||||
|
|
||||||
|
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
|
||||||
|
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
|
||||||
|
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
|
||||||
|
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
|
||||||
|
//#define ImDrawIdx unsigned int
|
||||||
|
|
||||||
|
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
|
||||||
|
//struct ImDrawList;
|
||||||
|
//struct ImDrawCmd;
|
||||||
|
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||||
|
//#define ImDrawCallback MyImDrawCallback
|
||||||
|
|
||||||
|
//---- Debug Tools: Macro to break in Debugger
|
||||||
|
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
||||||
|
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||||
|
//#define IM_DEBUG_BREAK __debugbreak()
|
||||||
|
|
||||||
|
//---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(),
|
||||||
|
// (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.)
|
||||||
|
// This adds a small runtime cost which is why it is not enabled by default.
|
||||||
|
//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
|
||||||
|
|
||||||
|
//---- Debug Tools: Enable slower asserts
|
||||||
|
//#define IMGUI_DEBUG_PARANOID
|
||||||
|
|
||||||
|
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
|
||||||
|
/*
|
||||||
|
namespace ImGui
|
||||||
|
{
|
||||||
|
void MyFunction(const char* name, const MyMatrix44& v);
|
||||||
|
}
|
||||||
|
*/
|
||||||
13444
libs/imgui/imgui.cpp
Normal file
13444
libs/imgui/imgui.cpp
Normal file
File diff suppressed because it is too large
Load Diff
3065
libs/imgui/imgui.h
Normal file
3065
libs/imgui/imgui.h
Normal file
File diff suppressed because it is too large
Load Diff
7969
libs/imgui/imgui_demo.cpp
Normal file
7969
libs/imgui/imgui_demo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
4164
libs/imgui/imgui_draw.cpp
Normal file
4164
libs/imgui/imgui_draw.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2975
libs/imgui/imgui_internal.h
Normal file
2975
libs/imgui/imgui_internal.h
Normal file
File diff suppressed because it is too large
Load Diff
4068
libs/imgui/imgui_tables.cpp
Normal file
4068
libs/imgui/imgui_tables.cpp
Normal file
File diff suppressed because it is too large
Load Diff
8394
libs/imgui/imgui_widgets.cpp
Normal file
8394
libs/imgui/imgui_widgets.cpp
Normal file
File diff suppressed because it is too large
Load Diff
627
libs/imgui/imstb_rectpack.h
Normal file
627
libs/imgui/imstb_rectpack.h
Normal file
@@ -0,0 +1,627 @@
|
|||||||
|
// [DEAR IMGUI]
|
||||||
|
// This is a slightly modified version of stb_rect_pack.h 1.01.
|
||||||
|
// Grep for [DEAR IMGUI] to find the changes.
|
||||||
|
//
|
||||||
|
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
|
||||||
|
// Sean Barrett 2014
|
||||||
|
//
|
||||||
|
// Useful for e.g. packing rectangular textures into an atlas.
|
||||||
|
// Does not do rotation.
|
||||||
|
//
|
||||||
|
// Before #including,
|
||||||
|
//
|
||||||
|
// #define STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
//
|
||||||
|
// in the file that you want to have the implementation.
|
||||||
|
//
|
||||||
|
// Not necessarily the awesomest packing method, but better than
|
||||||
|
// the totally naive one in stb_truetype (which is primarily what
|
||||||
|
// this is meant to replace).
|
||||||
|
//
|
||||||
|
// Has only had a few tests run, may have issues.
|
||||||
|
//
|
||||||
|
// More docs to come.
|
||||||
|
//
|
||||||
|
// No memory allocations; uses qsort() and assert() from stdlib.
|
||||||
|
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
|
||||||
|
//
|
||||||
|
// This library currently uses the Skyline Bottom-Left algorithm.
|
||||||
|
//
|
||||||
|
// Please note: better rectangle packers are welcome! Please
|
||||||
|
// implement them to the same API, but with a different init
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
// Credits
|
||||||
|
//
|
||||||
|
// Library
|
||||||
|
// Sean Barrett
|
||||||
|
// Minor features
|
||||||
|
// Martins Mozeiko
|
||||||
|
// github:IntellectualKitty
|
||||||
|
//
|
||||||
|
// Bugfixes / warning fixes
|
||||||
|
// Jeremy Jaussaud
|
||||||
|
// Fabian Giesen
|
||||||
|
//
|
||||||
|
// Version history:
|
||||||
|
//
|
||||||
|
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
|
||||||
|
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||||
|
// 0.99 (2019-02-07) warning fixes
|
||||||
|
// 0.11 (2017-03-03) return packing success/fail result
|
||||||
|
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||||
|
// 0.09 (2016-08-27) fix compiler warnings
|
||||||
|
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||||
|
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||||
|
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||||
|
// 0.05: added STBRP_ASSERT to allow replacing assert
|
||||||
|
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||||
|
// 0.01: initial release
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
//
|
||||||
|
// See end of file for license information.
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// INCLUDE SECTION
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||||
|
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||||
|
|
||||||
|
#define STB_RECT_PACK_VERSION 1
|
||||||
|
|
||||||
|
#ifdef STBRP_STATIC
|
||||||
|
#define STBRP_DEF static
|
||||||
|
#else
|
||||||
|
#define STBRP_DEF extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct stbrp_context stbrp_context;
|
||||||
|
typedef struct stbrp_node stbrp_node;
|
||||||
|
typedef struct stbrp_rect stbrp_rect;
|
||||||
|
|
||||||
|
typedef int stbrp_coord;
|
||||||
|
|
||||||
|
#define STBRP__MAXVAL 0x7fffffff
|
||||||
|
// Mostly for internal use, but this is the maximum supported coordinate value.
|
||||||
|
|
||||||
|
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||||
|
// Assign packed locations to rectangles. The rectangles are of type
|
||||||
|
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||||
|
// are 'num_rects' many of them.
|
||||||
|
//
|
||||||
|
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||||
|
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||||
|
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||||
|
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||||
|
// have the 'was_packed' flag set to 0.
|
||||||
|
//
|
||||||
|
// You should not try to access the 'rects' array from another thread
|
||||||
|
// while this function is running, as the function temporarily reorders
|
||||||
|
// the array while it executes.
|
||||||
|
//
|
||||||
|
// To pack into another rectangle, you need to call stbrp_init_target
|
||||||
|
// again. To continue packing into the same rectangle, you can call
|
||||||
|
// this function again. Calling this multiple times with multiple rect
|
||||||
|
// arrays will probably produce worse packing results than calling it
|
||||||
|
// a single time with the full rectangle array, but the option is
|
||||||
|
// available.
|
||||||
|
//
|
||||||
|
// The function returns 1 if all of the rectangles were successfully
|
||||||
|
// packed and 0 otherwise.
|
||||||
|
|
||||||
|
struct stbrp_rect
|
||||||
|
{
|
||||||
|
// reserved for your use:
|
||||||
|
int id;
|
||||||
|
|
||||||
|
// input:
|
||||||
|
stbrp_coord w, h;
|
||||||
|
|
||||||
|
// output:
|
||||||
|
stbrp_coord x, y;
|
||||||
|
int was_packed; // non-zero if valid packing
|
||||||
|
|
||||||
|
}; // 16 bytes, nominally
|
||||||
|
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||||
|
// Initialize a rectangle packer to:
|
||||||
|
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||||
|
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||||
|
//
|
||||||
|
// You must call this function every time you start packing into a new target.
|
||||||
|
//
|
||||||
|
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||||
|
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||||
|
// the call (or calls) finish.
|
||||||
|
//
|
||||||
|
// Note: to guarantee best results, either:
|
||||||
|
// 1. make sure 'num_nodes' >= 'width'
|
||||||
|
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||||
|
//
|
||||||
|
// If you don't do either of the above things, widths will be quantized to multiples
|
||||||
|
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||||
|
//
|
||||||
|
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||||
|
// may run out of temporary storage and be unable to pack some rectangles.
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||||
|
// Optionally call this function after init but before doing any packing to
|
||||||
|
// change the handling of the out-of-temp-memory scenario, described above.
|
||||||
|
// If you call init again, this will be reset to the default (false).
|
||||||
|
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||||
|
// Optionally select which packing heuristic the library should use. Different
|
||||||
|
// heuristics will produce better/worse results for different data sets.
|
||||||
|
// If you call init again, this will be reset to the default.
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STBRP_HEURISTIC_Skyline_default=0,
|
||||||
|
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||||
|
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// the details of the following structures don't matter to you, but they must
|
||||||
|
// be visible so you can handle the memory allocations for them
|
||||||
|
|
||||||
|
struct stbrp_node
|
||||||
|
{
|
||||||
|
stbrp_coord x,y;
|
||||||
|
stbrp_node *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stbrp_context
|
||||||
|
{
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int align;
|
||||||
|
int init_mode;
|
||||||
|
int heuristic;
|
||||||
|
int num_nodes;
|
||||||
|
stbrp_node *active_head;
|
||||||
|
stbrp_node *free_head;
|
||||||
|
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// IMPLEMENTATION SECTION
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
#ifndef STBRP_SORT
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define STBRP_SORT qsort
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STBRP_ASSERT
|
||||||
|
#include <assert.h>
|
||||||
|
#define STBRP_ASSERT assert
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define STBRP__NOTUSED(v) (void)(v)
|
||||||
|
#define STBRP__CDECL __cdecl
|
||||||
|
#else
|
||||||
|
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||||
|
#define STBRP__CDECL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STBRP__INIT_skyline = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||||
|
{
|
||||||
|
switch (context->init_mode) {
|
||||||
|
case STBRP__INIT_skyline:
|
||||||
|
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||||
|
context->heuristic = heuristic;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
STBRP_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||||
|
{
|
||||||
|
if (allow_out_of_mem)
|
||||||
|
// if it's ok to run out of memory, then don't bother aligning them;
|
||||||
|
// this gives better packing, but may fail due to OOM (even though
|
||||||
|
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||||
|
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||||
|
context->align = 1;
|
||||||
|
else {
|
||||||
|
// if it's not ok to run out of memory, then quantize the widths
|
||||||
|
// so that num_nodes is always enough nodes.
|
||||||
|
//
|
||||||
|
// I.e. num_nodes * align >= width
|
||||||
|
// align >= width / num_nodes
|
||||||
|
// align = ceil(width/num_nodes)
|
||||||
|
|
||||||
|
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i=0; i < num_nodes-1; ++i)
|
||||||
|
nodes[i].next = &nodes[i+1];
|
||||||
|
nodes[i].next = NULL;
|
||||||
|
context->init_mode = STBRP__INIT_skyline;
|
||||||
|
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||||
|
context->free_head = &nodes[0];
|
||||||
|
context->active_head = &context->extra[0];
|
||||||
|
context->width = width;
|
||||||
|
context->height = height;
|
||||||
|
context->num_nodes = num_nodes;
|
||||||
|
stbrp_setup_allow_out_of_mem(context, 0);
|
||||||
|
|
||||||
|
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||||
|
context->extra[0].x = 0;
|
||||||
|
context->extra[0].y = 0;
|
||||||
|
context->extra[0].next = &context->extra[1];
|
||||||
|
context->extra[1].x = (stbrp_coord) width;
|
||||||
|
context->extra[1].y = (1<<30);
|
||||||
|
context->extra[1].next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find minimum y position if it starts at x1
|
||||||
|
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||||
|
{
|
||||||
|
stbrp_node *node = first;
|
||||||
|
int x1 = x0 + width;
|
||||||
|
int min_y, visited_width, waste_area;
|
||||||
|
|
||||||
|
STBRP__NOTUSED(c);
|
||||||
|
|
||||||
|
STBRP_ASSERT(first->x <= x0);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// skip in case we're past the node
|
||||||
|
while (node->next->x <= x0)
|
||||||
|
++node;
|
||||||
|
#else
|
||||||
|
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STBRP_ASSERT(node->x <= x0);
|
||||||
|
|
||||||
|
min_y = 0;
|
||||||
|
waste_area = 0;
|
||||||
|
visited_width = 0;
|
||||||
|
while (node->x < x1) {
|
||||||
|
if (node->y > min_y) {
|
||||||
|
// raise min_y higher.
|
||||||
|
// we've accounted for all waste up to min_y,
|
||||||
|
// but we'll now add more waste for everything we've visted
|
||||||
|
waste_area += visited_width * (node->y - min_y);
|
||||||
|
min_y = node->y;
|
||||||
|
// the first time through, visited_width might be reduced
|
||||||
|
if (node->x < x0)
|
||||||
|
visited_width += node->next->x - x0;
|
||||||
|
else
|
||||||
|
visited_width += node->next->x - node->x;
|
||||||
|
} else {
|
||||||
|
// add waste area
|
||||||
|
int under_width = node->next->x - node->x;
|
||||||
|
if (under_width + visited_width > width)
|
||||||
|
under_width = width - visited_width;
|
||||||
|
waste_area += under_width * (min_y - node->y);
|
||||||
|
visited_width += under_width;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pwaste = waste_area;
|
||||||
|
return min_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int x,y;
|
||||||
|
stbrp_node **prev_link;
|
||||||
|
} stbrp__findresult;
|
||||||
|
|
||||||
|
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||||
|
{
|
||||||
|
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||||
|
stbrp__findresult fr;
|
||||||
|
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||||
|
|
||||||
|
// align to multiple of c->align
|
||||||
|
width = (width + c->align - 1);
|
||||||
|
width -= width % c->align;
|
||||||
|
STBRP_ASSERT(width % c->align == 0);
|
||||||
|
|
||||||
|
// if it can't possibly fit, bail immediately
|
||||||
|
if (width > c->width || height > c->height) {
|
||||||
|
fr.prev_link = NULL;
|
||||||
|
fr.x = fr.y = 0;
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = c->active_head;
|
||||||
|
prev = &c->active_head;
|
||||||
|
while (node->x + width <= c->width) {
|
||||||
|
int y,waste;
|
||||||
|
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||||
|
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||||
|
// bottom left
|
||||||
|
if (y < best_y) {
|
||||||
|
best_y = y;
|
||||||
|
best = prev;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// best-fit
|
||||||
|
if (y + height <= c->height) {
|
||||||
|
// can only use it if it first vertically
|
||||||
|
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||||
|
best_y = y;
|
||||||
|
best_waste = waste;
|
||||||
|
best = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev = &node->next;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||||
|
|
||||||
|
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||||
|
//
|
||||||
|
// e.g, if fitting
|
||||||
|
//
|
||||||
|
// ____________________
|
||||||
|
// |____________________|
|
||||||
|
//
|
||||||
|
// into
|
||||||
|
//
|
||||||
|
// | |
|
||||||
|
// | ____________|
|
||||||
|
// |____________|
|
||||||
|
//
|
||||||
|
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||||
|
//
|
||||||
|
// This makes BF take about 2x the time
|
||||||
|
|
||||||
|
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||||
|
tail = c->active_head;
|
||||||
|
node = c->active_head;
|
||||||
|
prev = &c->active_head;
|
||||||
|
// find first node that's admissible
|
||||||
|
while (tail->x < width)
|
||||||
|
tail = tail->next;
|
||||||
|
while (tail) {
|
||||||
|
int xpos = tail->x - width;
|
||||||
|
int y,waste;
|
||||||
|
STBRP_ASSERT(xpos >= 0);
|
||||||
|
// find the left position that matches this
|
||||||
|
while (node->next->x <= xpos) {
|
||||||
|
prev = &node->next;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||||
|
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||||
|
if (y + height <= c->height) {
|
||||||
|
if (y <= best_y) {
|
||||||
|
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||||
|
best_x = xpos;
|
||||||
|
//STBRP_ASSERT(y <= best_y); [DEAR IMGUI]
|
||||||
|
best_y = y;
|
||||||
|
best_waste = waste;
|
||||||
|
best = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tail = tail->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fr.prev_link = best;
|
||||||
|
fr.x = best_x;
|
||||||
|
fr.y = best_y;
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||||
|
{
|
||||||
|
// find best position according to heuristic
|
||||||
|
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||||
|
stbrp_node *node, *cur;
|
||||||
|
|
||||||
|
// bail if:
|
||||||
|
// 1. it failed
|
||||||
|
// 2. the best node doesn't fit (we don't always check this)
|
||||||
|
// 3. we're out of memory
|
||||||
|
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||||
|
res.prev_link = NULL;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// on success, create new node
|
||||||
|
node = context->free_head;
|
||||||
|
node->x = (stbrp_coord) res.x;
|
||||||
|
node->y = (stbrp_coord) (res.y + height);
|
||||||
|
|
||||||
|
context->free_head = node->next;
|
||||||
|
|
||||||
|
// insert the new node into the right starting point, and
|
||||||
|
// let 'cur' point to the remaining nodes needing to be
|
||||||
|
// stiched back in
|
||||||
|
|
||||||
|
cur = *res.prev_link;
|
||||||
|
if (cur->x < res.x) {
|
||||||
|
// preserve the existing one, so start testing with the next one
|
||||||
|
stbrp_node *next = cur->next;
|
||||||
|
cur->next = node;
|
||||||
|
cur = next;
|
||||||
|
} else {
|
||||||
|
*res.prev_link = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from here, traverse cur and free the nodes, until we get to one
|
||||||
|
// that shouldn't be freed
|
||||||
|
while (cur->next && cur->next->x <= res.x + width) {
|
||||||
|
stbrp_node *next = cur->next;
|
||||||
|
// move the current node to the free list
|
||||||
|
cur->next = context->free_head;
|
||||||
|
context->free_head = cur;
|
||||||
|
cur = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stitch the list back in
|
||||||
|
node->next = cur;
|
||||||
|
|
||||||
|
if (cur->x < res.x + width)
|
||||||
|
cur->x = (stbrp_coord) (res.x + width);
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
cur = context->active_head;
|
||||||
|
while (cur->x < context->width) {
|
||||||
|
STBRP_ASSERT(cur->x < cur->next->x);
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
STBRP_ASSERT(cur->next == NULL);
|
||||||
|
|
||||||
|
{
|
||||||
|
int count=0;
|
||||||
|
cur = context->active_head;
|
||||||
|
while (cur) {
|
||||||
|
cur = cur->next;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
cur = context->free_head;
|
||||||
|
while (cur) {
|
||||||
|
cur = cur->next;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
STBRP_ASSERT(count == context->num_nodes+2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||||
|
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||||
|
if (p->h > q->h)
|
||||||
|
return -1;
|
||||||
|
if (p->h < q->h)
|
||||||
|
return 1;
|
||||||
|
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||||
|
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||||
|
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||||
|
{
|
||||||
|
int i, all_rects_packed = 1;
|
||||||
|
|
||||||
|
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||||
|
for (i=0; i < num_rects; ++i) {
|
||||||
|
rects[i].was_packed = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort according to heuristic
|
||||||
|
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||||
|
|
||||||
|
for (i=0; i < num_rects; ++i) {
|
||||||
|
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||||
|
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||||
|
} else {
|
||||||
|
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||||
|
if (fr.prev_link) {
|
||||||
|
rects[i].x = (stbrp_coord) fr.x;
|
||||||
|
rects[i].y = (stbrp_coord) fr.y;
|
||||||
|
} else {
|
||||||
|
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsort
|
||||||
|
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||||
|
|
||||||
|
// set was_packed flags and all_rects_packed status
|
||||||
|
for (i=0; i < num_rects; ++i) {
|
||||||
|
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||||
|
if (!rects[i].was_packed)
|
||||||
|
all_rects_packed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the all_rects_packed status
|
||||||
|
return all_rects_packed;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and 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 notice and 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.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
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 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.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
1447
libs/imgui/imstb_textedit.h
Normal file
1447
libs/imgui/imstb_textedit.h
Normal file
File diff suppressed because it is too large
Load Diff
5085
libs/imgui/imstb_truetype.h
Normal file
5085
libs/imgui/imstb_truetype.h
Normal file
File diff suppressed because it is too large
Load Diff
96
src/GPData.cpp
Normal file
96
src/GPData.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#include "GPData.h"
|
||||||
|
|
||||||
|
#include <GL/glew.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace gpgui {
|
||||||
|
namespace data {
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
VertexData GetRectData(float x, float y, float dx, float dy, float color) {
|
||||||
|
return {
|
||||||
|
dx, dy, color,
|
||||||
|
x, dy, color,
|
||||||
|
dx, y, color,
|
||||||
|
|
||||||
|
x, dy, color,
|
||||||
|
x, y, color,
|
||||||
|
dx, y, color,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
VertexData GetCircleData(float centerX, float centerY, float radius, float color, int precision) {
|
||||||
|
VertexData vertexData;
|
||||||
|
vertexData.reserve(9 * precision);
|
||||||
|
|
||||||
|
for (int i = 0; i < precision; i++) {
|
||||||
|
float theta = 2.0f * M_PI * float(i) / float(precision); // get the current angle
|
||||||
|
float thetaNext = 2.0f * M_PI * float(i + 1) / float(precision); // get the next angle
|
||||||
|
|
||||||
|
float x = radius * cosf(theta); // calculate the x component
|
||||||
|
float y = radius * sinf(theta); // calculate the y component
|
||||||
|
|
||||||
|
float dx = radius * cosf(thetaNext); // calculate the next x component
|
||||||
|
float dy = radius * sinf(thetaNext); // calculate the next y component
|
||||||
|
|
||||||
|
vertexData.insert(vertexData.end(), {
|
||||||
|
x + centerX, y + centerY, color,
|
||||||
|
centerX, centerY, color,
|
||||||
|
dx + centerX, dy + centerY, color,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return vertexData;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetIntColor(const Color& color) {
|
||||||
|
float out_color;
|
||||||
|
int temp_color = color.red << 24 | color.green << 16 | color.blue << 8 | color.alpha;
|
||||||
|
std::memcpy(&out_color, &temp_color, sizeof(float));
|
||||||
|
return out_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawData GetDrawData(const VertexData& vertexData) {
|
||||||
|
DrawData drawData;
|
||||||
|
|
||||||
|
glGenVertexArrays(1, &drawData.vao);
|
||||||
|
glGenBuffers(1, &drawData.vbo);
|
||||||
|
|
||||||
|
drawData.vertexCount = vertexData.size() / 3;
|
||||||
|
|
||||||
|
constexpr GLsizei stride = sizeof(float) * 3;
|
||||||
|
|
||||||
|
glBindVertexArray(drawData.vao);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, drawData.vbo);
|
||||||
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, (void*)0);
|
||||||
|
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, stride, (void*)(sizeof(float) * 2));
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(float), vertexData.data(), GL_STATIC_DRAW);
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
return drawData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateData(DrawData& buffer, const VertexData& newData) {
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, newData.size() * sizeof(float), newData.data(), GL_STATIC_DRAW);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
// OMFG 1 hour of debugging to add this line
|
||||||
|
buffer.vertexCount = newData.size() / 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawVertexData(const DrawData& vertexData) {
|
||||||
|
glBindVertexArray(vertexData.vao);
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, vertexData.vertexCount);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace data
|
||||||
|
} // namespace gpgui
|
||||||
454
src/GPGui.cpp
Normal file
454
src/GPGui.cpp
Normal file
@@ -0,0 +1,454 @@
|
|||||||
|
#include "GPGui.h"
|
||||||
|
#include "GPMusic.h"
|
||||||
|
#include "GPRenderer.h"
|
||||||
|
#include "GPData.h"
|
||||||
|
#include "GPSave.h"
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
namespace gpgui {
|
||||||
|
namespace gui {
|
||||||
|
|
||||||
|
using Song = save::Song;
|
||||||
|
using ChordSave = save::ChordSave;
|
||||||
|
|
||||||
|
using ChordType = music::ChordType;
|
||||||
|
using ChordOffsets = music::ChordOffsets;
|
||||||
|
using Tab = music::Tab;
|
||||||
|
using Note = music::Note;
|
||||||
|
|
||||||
|
typedef std::shared_ptr<Song> SongPtr;
|
||||||
|
|
||||||
|
static bool pianoChordOnGuitar = true;
|
||||||
|
|
||||||
|
static int currentCapo = 0;
|
||||||
|
|
||||||
|
static const int CAPO_MIN = 0;
|
||||||
|
static const int CAPO_MAX = 10;
|
||||||
|
|
||||||
|
static ChordSave currentChord;
|
||||||
|
|
||||||
|
static int fretMax = 5;
|
||||||
|
static int currentOctave = 2;
|
||||||
|
|
||||||
|
static std::vector<SongPtr> loadedSongs;
|
||||||
|
static SongPtr editSong = nullptr;
|
||||||
|
|
||||||
|
constexpr ImVec4 SAVE_COLOR{ 0, 0.5, 0, 1 };
|
||||||
|
constexpr ImVec4 SAVE_HOVERED_COLOR{ 0, 0.7, 0, 1 };
|
||||||
|
|
||||||
|
constexpr ImVec4 DELETE_COLOR{ 0.5, 0, 0, 1 };
|
||||||
|
constexpr ImVec4 DELETE_HOVERED_COLOR{ 0.7, 0, 0, 1 };
|
||||||
|
|
||||||
|
static void ApplyPianoChord(ChordType ct, std::uint8_t note, int octave, const ChordOffsets& notes) {
|
||||||
|
renderer::ClearKeyboard();
|
||||||
|
for (int i = 0; i < notes.size(); i++) {
|
||||||
|
if (notes[i] == data::EMPTY_NOTE)
|
||||||
|
continue;
|
||||||
|
renderer::SetKeyHighlight(notes[i], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ApplyTab(const Tab& tab) {
|
||||||
|
renderer::ClearTab();
|
||||||
|
for (int i = 0; i < tab.size(); i++) {
|
||||||
|
renderer::SetTab(i, tab[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ApplyTabPreview(const Tab& tab) {
|
||||||
|
ApplyTab(tab);
|
||||||
|
renderer::ClearKeyboard();
|
||||||
|
for (int i = 0; i < tab.size(); i++) {
|
||||||
|
if (tab[i] == data::EMPTY_TAB)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (tab[i] == 0) {
|
||||||
|
renderer::SetKeyHighlight(music::GetStringOffset(i) + currentCapo, true);
|
||||||
|
} else {
|
||||||
|
renderer::SetKeyHighlight(music::GetStringOffset(i) + tab[i], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Tab GetTabChord(ChordType ct, std::uint8_t note, const ChordOffsets& notes) {
|
||||||
|
if (pianoChordOnGuitar) {
|
||||||
|
return music::FindPianoChord(notes, currentCapo, currentChord.fretMax);
|
||||||
|
} else {
|
||||||
|
return music::FindChord(music::GetChord(Note(note), ct), currentCapo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ApplyGuitarChord(ChordType ct, std::uint8_t note, int octave, const ChordOffsets& notes) {
|
||||||
|
Tab tab = GetTabChord(ct, note, notes);
|
||||||
|
ApplyTab(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InverseChord(ChordOffsets& notes) {
|
||||||
|
for (int i = 0; i < currentChord.inversion; i++) {
|
||||||
|
std::uint8_t lastNote = notes[notes[3] == data::EMPTY_NOTE ? 2 : 3] - 12;
|
||||||
|
std::uint8_t count = notes[3] == data::EMPTY_NOTE ? 2 : 3;
|
||||||
|
for (int j = count; j > 0; j--) {
|
||||||
|
notes[j] = notes[j - 1];
|
||||||
|
}
|
||||||
|
notes[0] = lastNote;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ChordOffsets OffsetsToNote(const ChordOffsets& offsets, int octave) {
|
||||||
|
ChordOffsets notes;
|
||||||
|
for (int i = 0; i < offsets.size(); i++) {
|
||||||
|
if (offsets[i] == data::EMPTY_NOTE) {
|
||||||
|
notes[i] = data::EMPTY_NOTE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
notes[i] = currentChord.note + offsets[i] + 12 * octave;
|
||||||
|
}
|
||||||
|
return notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ApplyChord(ChordType ct, std::uint8_t note, int octave) {
|
||||||
|
ChordOffsets offsets = GetChordOffsets(Note(note), ct);
|
||||||
|
ChordOffsets notes = OffsetsToNote(offsets, octave);
|
||||||
|
|
||||||
|
InverseChord(notes);
|
||||||
|
ApplyPianoChord(ct, note, octave, notes);
|
||||||
|
ApplyGuitarChord(ct, note, octave, notes);
|
||||||
|
|
||||||
|
renderer::UpdateBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RefreshRendering() {
|
||||||
|
if (currentChord.note != Note::TOTAL && currentChord.type != ChordType::COUNT)
|
||||||
|
ApplyChord(currentChord.type, static_cast<std::uint8_t>(currentChord.note), currentChord.octave);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RenderChordButtons(ChordType ct) {
|
||||||
|
for (int i = 0; i < Note::TOTAL; i++) {
|
||||||
|
if (ImGui::Button(ToString(Note(i)).c_str())) {
|
||||||
|
currentChord.note = Note(i);
|
||||||
|
currentChord.type = ct;
|
||||||
|
RefreshRendering();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
}
|
||||||
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 100);
|
||||||
|
if (ImGui::Button("Inversion")) {
|
||||||
|
currentChord.inversion = (currentChord.inversion + 1) % 4;
|
||||||
|
RefreshRendering();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text(": %i", currentChord.inversion);
|
||||||
|
if (ImGui::SliderInt("Octave", ¤tOctave, 1, 3) && currentChord.note != Note::TOTAL) {
|
||||||
|
currentChord.octave = currentOctave;
|
||||||
|
RefreshRendering();
|
||||||
|
}
|
||||||
|
if (ImGui::SliderInt("Fret max", &fretMax, 3, 12)) {
|
||||||
|
currentChord.fretMax = fretMax;
|
||||||
|
RefreshRendering();
|
||||||
|
}
|
||||||
|
if (currentChord.note != Note::TOTAL) {
|
||||||
|
ImGui::Text("Accord actuel : %s %s", ToString(Note(currentChord.note)).c_str(), ToString(ct).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RenderChordsTab() {
|
||||||
|
if (ImGui::BeginTabItem("Accords")) {
|
||||||
|
ImGui::BeginTabBar("Chords");
|
||||||
|
for (int i = 0; i < static_cast<int>(ChordType::COUNT); i++) {
|
||||||
|
ChordType ct = ChordType(i);
|
||||||
|
if (ImGui::BeginTabItem(ToString(ct).c_str())) {
|
||||||
|
ImGui::Text("%s :", ToString(ct).c_str());
|
||||||
|
RenderChordButtons(ct);
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndTabBar();
|
||||||
|
if (editSong != nullptr) {
|
||||||
|
if (ImGui::Button("Ajouter l'accord")) {
|
||||||
|
if (currentChord.note != Note::TOTAL) {
|
||||||
|
editSong->chords.push_back(currentChord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RenderOptions() {
|
||||||
|
if (ImGui::BeginTabItem("Options")) {
|
||||||
|
if (ImGui::Checkbox("Accords Guitaro-Piano ?", &pianoChordOnGuitar)) {
|
||||||
|
currentChord.guitaroPiano = pianoChordOnGuitar;
|
||||||
|
RefreshRendering();
|
||||||
|
}
|
||||||
|
if (editSong != nullptr) {
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
}
|
||||||
|
if (ImGui::InputInt("Capo", ¤tCapo, 1, 10)) {
|
||||||
|
currentCapo = std::clamp(currentCapo, CAPO_MIN, CAPO_MAX);
|
||||||
|
RefreshRendering();
|
||||||
|
renderer::SetCapoPos(currentCapo);
|
||||||
|
}
|
||||||
|
if (editSong != nullptr) {
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RenderSaveSongButton(const SongPtr& song) {
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, SAVE_COLOR);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, SAVE_HOVERED_COLOR);
|
||||||
|
if (ImGui::Button(std::string("Enregistrer##" + song->title).c_str())) {
|
||||||
|
Song saveSong = *song;
|
||||||
|
save::SaveSongToFile(saveSong, saveSong.title + ".gp");
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool RenderDeleteSongButton(const SongPtr& song) {
|
||||||
|
if (song == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
const std::string popupId = "DeleteSongConfirm" + song->title;
|
||||||
|
|
||||||
|
bool deleted = false;
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, DELETE_COLOR);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, DELETE_HOVERED_COLOR);
|
||||||
|
if (ImGui::Button(std::string("Supprimer##" + song->title).c_str())) {
|
||||||
|
ImGui::OpenPopup(popupId.c_str());
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopup(popupId.c_str())) {
|
||||||
|
ImGui::Text("Supprimer ?");
|
||||||
|
if (ImGui::Button("Oui")) {
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
deleted = true;
|
||||||
|
std::string fileName = song->title + ".gp";
|
||||||
|
if (fs::exists(fileName)) { // removing file if it exists
|
||||||
|
fs::remove(fileName);
|
||||||
|
}
|
||||||
|
auto it = std::find(loadedSongs.begin(), loadedSongs.end(), song);
|
||||||
|
loadedSongs.erase(it);
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, SAVE_COLOR);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, SAVE_HOVERED_COLOR);
|
||||||
|
if (ImGui::Button("Non")) {
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
ImGui::EndPopup();
|
||||||
|
} else {
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
}
|
||||||
|
return deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RenderSongs() {
|
||||||
|
if (loadedSongs.empty()) {
|
||||||
|
ImGui::Text("Aucune chanson chargée");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ImGui::BeginChild("Songs");
|
||||||
|
for (auto& song : loadedSongs) {
|
||||||
|
ImGui::Text("%s (capo %i)", song->title.c_str(), song->capo);
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (song == editSong) {
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
ImGui::Button("Séléctionnée");
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
} else {
|
||||||
|
if (ImGui::Button(std::string("Sélectionner##" + song->title).c_str())) {
|
||||||
|
editSong = song;
|
||||||
|
currentCapo = song->capo;
|
||||||
|
renderer::SetCapoPos(currentCapo);
|
||||||
|
RefreshRendering();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
RenderSaveSongButton(song);
|
||||||
|
ImGui::SameLine();
|
||||||
|
RenderDeleteSongButton(song);
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RenderNewSongPopup() {
|
||||||
|
if (ImGui::BeginPopup("##New Song Popup")) {
|
||||||
|
static char buffer[512];
|
||||||
|
static int songCapo = 0;
|
||||||
|
ImGui::InputText("Nom de la chanson", buffer, sizeof(buffer));
|
||||||
|
ImGui::InputInt("Capo", &songCapo, 1);
|
||||||
|
songCapo = std::clamp(songCapo, CAPO_MIN, CAPO_MAX);
|
||||||
|
if (ImGui::Button("Créer une nouvelle chanson")) {
|
||||||
|
loadedSongs.push_back(std::make_shared<Song>(buffer, static_cast<save::CapoPosType>(songCapo)));
|
||||||
|
// resetting buffers
|
||||||
|
songCapo = 0;
|
||||||
|
std::fill(std::begin(buffer), std::end(buffer), 0);
|
||||||
|
// closing popup
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AddSongsInDirectory() {
|
||||||
|
for (const auto& entry : fs::directory_iterator(".")) {
|
||||||
|
auto path = entry.path();
|
||||||
|
if (path.extension().string() == ".gp") {
|
||||||
|
std::string fileName = path.filename().string();
|
||||||
|
Song newSong = save::LoadSongFromFile(fileName);
|
||||||
|
if (newSong.title.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto it = std::find_if(loadedSongs.begin(), loadedSongs.end(), [&newSong](SongPtr song) {
|
||||||
|
return newSong.title == song->title;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it == loadedSongs.end()) { // add only if does not already exist
|
||||||
|
loadedSongs.push_back(std::make_shared<Song>(newSong));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RenderSongsTab() {
|
||||||
|
if (ImGui::BeginTabItem("Chansons")) {
|
||||||
|
if (ImGui::Button("Nouveau")) {
|
||||||
|
ImGui::OpenPopup("##New Song Popup");
|
||||||
|
}
|
||||||
|
RenderNewSongPopup();
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Actualiser")) {
|
||||||
|
AddSongsInDirectory();
|
||||||
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
RenderSongs();
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int HashTab(const Tab& tab) {
|
||||||
|
return std::hash<unsigned int>()(tab[0] & 0xF << 20 | tab[1] & 0xF << 16
|
||||||
|
| tab[2] & 0xF << 12 | tab[3] & 0xF << 8 | tab[4] & 0xF << 4
|
||||||
|
| tab[5] & 0xF);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RenderSongFrames() {
|
||||||
|
static int eraseIndex = 0;
|
||||||
|
for (int i = 0; i < editSong->chords.size(); i++) {
|
||||||
|
ImGui::BeginChildFrame(100 + i * 100, ImVec2(200, 190));
|
||||||
|
ImGui::Text(music::ToString(editSong->chords[i]).c_str());
|
||||||
|
if (i > 0) {
|
||||||
|
if (ImGui::Button("<-")) {
|
||||||
|
auto previousTab = editSong->chords[i - 1];
|
||||||
|
editSong->chords[i - 1] = editSong->chords[i];
|
||||||
|
editSong->chords[i] = previousTab;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
}
|
||||||
|
if (i < editSong->chords.size() - 1) {
|
||||||
|
if (ImGui::Button("->")) {
|
||||||
|
auto nextTab = editSong->chords[i + 1];
|
||||||
|
editSong->chords[i + 1] = editSong->chords[i];
|
||||||
|
editSong->chords[i] = nextTab;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ImGui::NewLine();
|
||||||
|
}
|
||||||
|
if (ImGui::Button("Visualiser")) {
|
||||||
|
currentChord = editSong->chords[i];
|
||||||
|
currentOctave = currentChord.octave;
|
||||||
|
pianoChordOnGuitar = currentChord.guitaroPiano;
|
||||||
|
RefreshRendering();
|
||||||
|
}
|
||||||
|
if (ImGui::Button("Supprimer")) {
|
||||||
|
ImGui::OpenPopup("EraseTab");
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopup("EraseTab")) {
|
||||||
|
ImGui::Text("Supprimer ?");
|
||||||
|
if (ImGui::Button("Oui")) {
|
||||||
|
editSong->chords.erase(editSong->chords.begin() + i);
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Non")) {
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
ImGui::EndChildFrame();
|
||||||
|
ImGui::SameLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RenderEditTab() {
|
||||||
|
if (ImGui::BeginTabItem("Edition")) {
|
||||||
|
if (editSong == nullptr) {
|
||||||
|
ImGui::Text("Sélectionnez une chanson pour commencer");
|
||||||
|
} else {
|
||||||
|
ImGui::Text("Edition de %s (capo %i)", editSong->title.c_str(), editSong->capo);
|
||||||
|
|
||||||
|
ImGui::BeginChild("SongContent", {}, false, ImGuiWindowFlags_HorizontalScrollbar);
|
||||||
|
RenderSongFrames();
|
||||||
|
ImGui::NewLine();
|
||||||
|
RenderSaveSongButton(editSong);
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (RenderDeleteSongButton(editSong)) {
|
||||||
|
editSong = nullptr;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Terminé")) {
|
||||||
|
editSong = nullptr;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::EndChild();
|
||||||
|
}
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RenderInfos() {
|
||||||
|
if (ImGui::BeginTabItem("Infos")) {
|
||||||
|
ImGui::Text("FPS : %i", (int) std::ceil(ImGui::GetIO().Framerate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RenderTabs() {
|
||||||
|
ImGui::BeginTabBar("MainTab");
|
||||||
|
RenderChordsTab();
|
||||||
|
RenderEditTab();
|
||||||
|
RenderSongsTab();
|
||||||
|
RenderOptions();
|
||||||
|
RenderInfos();
|
||||||
|
ImGui::EndTabBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Render() {
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui::Begin("Piano", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove);
|
||||||
|
ImGui::SetWindowPos({ 0, 0 }, ImGuiCond_Always);
|
||||||
|
ImGui::SetWindowSize({ io.DisplaySize.x, io.DisplaySize.y * 0.4f }, ImGuiCond_Always);
|
||||||
|
RenderTabs();
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init() {
|
||||||
|
AddSongsInDirectory();
|
||||||
|
currentChord.octave = currentOctave; // adjust the slider
|
||||||
|
currentChord.fretMax = fretMax;
|
||||||
|
currentChord.guitaroPiano = pianoChordOnGuitar;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace gui
|
||||||
|
} // namespace gpgui
|
||||||
196
src/GPMusic.cpp
Normal file
196
src/GPMusic.cpp
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
#include "GPMusic.h"
|
||||||
|
#include "GPData.h"
|
||||||
|
#include "GPSave.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(A) sizeof(A) / sizeof(A[0])
|
||||||
|
|
||||||
|
namespace gpgui {
|
||||||
|
namespace music {
|
||||||
|
|
||||||
|
using ChordSave = save::ChordSave;
|
||||||
|
|
||||||
|
|
||||||
|
// The positions of strings in a piano keyboard
|
||||||
|
static const std::uint8_t Cordes[] = {
|
||||||
|
7, 12, 17, 22, 26, 31
|
||||||
|
};
|
||||||
|
|
||||||
|
int GetStringOffset(int string) {
|
||||||
|
return Cordes[string];
|
||||||
|
}
|
||||||
|
|
||||||
|
Note GetNote(std::uint8_t touche) {
|
||||||
|
return Note(touche % 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t GetOctave(std::uint8_t touche) {
|
||||||
|
return touche / 12 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ToString(Note note) {
|
||||||
|
switch (note) {
|
||||||
|
case A:
|
||||||
|
return "A";
|
||||||
|
case Bb:
|
||||||
|
return "A#";
|
||||||
|
case B:
|
||||||
|
return "B";
|
||||||
|
case C:
|
||||||
|
return "C";
|
||||||
|
case Db:
|
||||||
|
return "C#";
|
||||||
|
case D:
|
||||||
|
return "D";
|
||||||
|
case Eb:
|
||||||
|
return "D#";
|
||||||
|
case E:
|
||||||
|
return "E";
|
||||||
|
case F:
|
||||||
|
return "F";
|
||||||
|
case Gb:
|
||||||
|
return "F#";
|
||||||
|
case G:
|
||||||
|
return "G";
|
||||||
|
case Ab:
|
||||||
|
return "G#";
|
||||||
|
default:
|
||||||
|
return "wtf";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ToString(ChordType chord) {
|
||||||
|
switch (chord) {
|
||||||
|
case ChordType::Major:
|
||||||
|
return "Majeur";
|
||||||
|
case ChordType::Minor:
|
||||||
|
return "Mineur";
|
||||||
|
case ChordType::Dim:
|
||||||
|
return "Dim";
|
||||||
|
case ChordType::Sus:
|
||||||
|
return "Sus";
|
||||||
|
case ChordType::Major7:
|
||||||
|
return "Majeur7";
|
||||||
|
case ChordType::Minor7:
|
||||||
|
return "Mineur7";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ToString(const Tab& tab) {
|
||||||
|
std::string result;
|
||||||
|
for (int fret : tab) {
|
||||||
|
if (fret == data::EMPTY_TAB) {
|
||||||
|
result += "X";
|
||||||
|
} else {
|
||||||
|
result += std::to_string(fret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ToString(const ChordSave& chord) {
|
||||||
|
return ToString(chord.note) + " " + ToString(chord.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isChord(int pos, const Chord& chord) {
|
||||||
|
return GetNote(pos) == chord[0] || GetNote(pos) == chord[1] || GetNote(pos) == chord[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
Tab FindChord(const Chord& chord, int capo) {
|
||||||
|
Tab tab;
|
||||||
|
tab.fill(data::EMPTY_TAB);
|
||||||
|
|
||||||
|
for (int i = 0; i < tab.size(); i++) {
|
||||||
|
int offset = 0;
|
||||||
|
int cordePos = Cordes[i];
|
||||||
|
|
||||||
|
while (!isChord(cordePos + offset + capo, chord)) {
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
tab[i] = offset;
|
||||||
|
// if unable to have a note beacuse of the capo :
|
||||||
|
if (offset > 12 - capo) {
|
||||||
|
tab[i] = data::EMPTY_TAB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tab FindFakeChord(const Chord& chord, int capo, int pCorde) {
|
||||||
|
Tab tab;
|
||||||
|
tab.fill(data::EMPTY_TAB);
|
||||||
|
|
||||||
|
for (int i = pCorde; i < tab.size(); i++) {
|
||||||
|
int offset = 0;
|
||||||
|
int cordePos = Cordes[i];
|
||||||
|
|
||||||
|
while (GetNote(cordePos + offset + capo) != chord[(i - pCorde) % 3]) {
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
tab[i] = offset;
|
||||||
|
if (offset > 12 - capo) {
|
||||||
|
tab[i] = data::EMPTY_TAB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tab FindPianoChord(const ChordOffsets& notes, int capo, int fretThreshold) {
|
||||||
|
Tab tab;
|
||||||
|
tab.fill(data::EMPTY_TAB);
|
||||||
|
int pCorde = 0;
|
||||||
|
for (int noteIndex = 0; noteIndex < notes.size(); noteIndex++) {
|
||||||
|
int note = notes[noteIndex];
|
||||||
|
if (note == data::EMPTY_NOTE) // empty chord
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//searching a string
|
||||||
|
for (int i = pCorde; i < sizeof(Cordes); i++) {
|
||||||
|
if (note >= Cordes[i] + capo) {
|
||||||
|
int fret = note - (Cordes[i] + capo);
|
||||||
|
if (fret <= fretThreshold - capo) {
|
||||||
|
pCorde = i;
|
||||||
|
tab[i] = note - Cordes[i];
|
||||||
|
|
||||||
|
if (note - (Cordes[i] + capo) == 0) // the fret is on the capo
|
||||||
|
tab[i] = 0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
Chord GetChord(Note note, ChordType type) {
|
||||||
|
ChordOffsets offsets = GetChordOffsets(note, type);
|
||||||
|
return { Note(note + offsets[0] % Note::TOTAL), Note(note + offsets[1] % Note::TOTAL), Note(note + offsets[2] % Note::TOTAL), offsets[3] == Note(data::EMPTY_NOTE) ? Note(data::EMPTY_NOTE) : Note(note + offsets[3]) };
|
||||||
|
}
|
||||||
|
|
||||||
|
ChordOffsets GetChordOffsets(Note note, ChordType type) {
|
||||||
|
switch (type) {
|
||||||
|
case ChordType::Major:
|
||||||
|
return { 0, 4, 7, data::EMPTY_NOTE };
|
||||||
|
case ChordType::Minor:
|
||||||
|
return { 0, 3, 7, data::EMPTY_NOTE };
|
||||||
|
case ChordType::Dim:
|
||||||
|
return { 0, 3, 6, data::EMPTY_NOTE };
|
||||||
|
case ChordType::Sus:
|
||||||
|
return { 0, 5, 7, data::EMPTY_NOTE };
|
||||||
|
case ChordType::Major7:
|
||||||
|
return { 0, 4, 7, 10 };
|
||||||
|
case ChordType::Minor7:
|
||||||
|
return { 0, 3, 7, 10 };
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace music
|
||||||
|
} // namespace gpgui
|
||||||
370
src/GPRenderer.cpp
Normal file
370
src/GPRenderer.cpp
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
#include "GPRenderer.h"
|
||||||
|
#include "GPData.h"
|
||||||
|
#include "ShaderProgram.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace gpgui {
|
||||||
|
namespace renderer {
|
||||||
|
|
||||||
|
using VertexData = data::VertexData;
|
||||||
|
using Color = data::Color;
|
||||||
|
|
||||||
|
class GPShader : public ShaderProgram {
|
||||||
|
public:
|
||||||
|
GPShader() : ShaderProgram() {}
|
||||||
|
|
||||||
|
virtual void GetAllUniformLocation() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const char vertexShader[] = R"(
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
layout (location = 0) in vec2 Position;
|
||||||
|
layout (location = 1) in int Color;
|
||||||
|
|
||||||
|
flat out int pass_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
pass_color = Color;
|
||||||
|
gl_Position = vec4(Position.x * 2.0 - 1.0, Position.y * 2.0 - 1.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
|
||||||
|
const char fragmentShader[] = R"(
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
flat in int pass_color;
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
color = vec4(pass_color >> 24 & 0xFF, pass_color >> 16 & 0xFF, pass_color >> 8 & 0xFF, pass_color & 0xFF) / 255.0;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
enum KeyType : std::uint8_t {
|
||||||
|
Major = 0,
|
||||||
|
Minor
|
||||||
|
};
|
||||||
|
|
||||||
|
static data::DrawData keyData, stringsData;
|
||||||
|
static GPShader gpShader;
|
||||||
|
static std::array<std::uint8_t, 7> highlitedKeys; // not 6 to avoid visual glitches with the strings
|
||||||
|
static std::array<std::uint8_t, 6> highlitedStrings;
|
||||||
|
static int capo = 0;
|
||||||
|
|
||||||
|
constexpr int KEY_NUMBER = 52;
|
||||||
|
constexpr float KEYBOARD_HEIGHT = 0.3;
|
||||||
|
constexpr float TAB_HEIGHT = 0.3;
|
||||||
|
|
||||||
|
bool IsKeyHighlited(int key) {
|
||||||
|
bool result = (highlitedKeys[key / 8] >> (7 - (key % 8))) & 0x1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetKeyHighlight(int key, bool highlight) {
|
||||||
|
bool value = IsKeyHighlited(key);
|
||||||
|
if (value == highlight)
|
||||||
|
return;
|
||||||
|
highlitedKeys[key / 8] ^= (1 << (7 - (key % 8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTab(int tab, int fret) {
|
||||||
|
highlitedStrings[tab] = static_cast<std::uint8_t>(fret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearKeyboard() {
|
||||||
|
highlitedKeys.fill(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearTab() {
|
||||||
|
highlitedStrings.fill(data::EMPTY_TAB);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetKeyFromWhite(int whiteIndex) {
|
||||||
|
int octave = whiteIndex / 7;
|
||||||
|
int whiteKey = whiteIndex % 7;
|
||||||
|
int offset = whiteKey * 2;
|
||||||
|
if (whiteKey >= 2)
|
||||||
|
offset--; // first hole
|
||||||
|
if (whiteKey >= 5)
|
||||||
|
offset--; // second hole
|
||||||
|
return octave * 12 + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetKeyFromBlack(int blackIndex) {
|
||||||
|
int octave = blackIndex / 7;
|
||||||
|
int blackKey = blackIndex % 7;
|
||||||
|
int offset = blackIndex * 2;
|
||||||
|
// too lazy to do something good
|
||||||
|
switch (blackKey) {
|
||||||
|
case 0: {
|
||||||
|
offset = 11;
|
||||||
|
octave--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
offset = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
offset = 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
offset = 6;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 6: {
|
||||||
|
offset = 9;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return octave * 12 + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCapoPos(int capoPos) {
|
||||||
|
capo = capoPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
VertexData GetKeyboardData() {
|
||||||
|
VertexData vertexData;
|
||||||
|
vertexData.reserve(2048);
|
||||||
|
|
||||||
|
constexpr Color WHITE{ 255, 255, 255 };
|
||||||
|
constexpr Color BLACK{ 0, 0, 0 };
|
||||||
|
constexpr Color GREEN{ 132, 255, 0 };
|
||||||
|
constexpr Color DARK_GREEN{ 57, 190, 0 };
|
||||||
|
|
||||||
|
float whiteColor = data::GetIntColor(WHITE);
|
||||||
|
|
||||||
|
// white background
|
||||||
|
VertexData backgroundData = data::GetRectData(0.0f, 0.0f, 1.0f, KEYBOARD_HEIGHT, whiteColor);
|
||||||
|
vertexData.insert(vertexData.end(), backgroundData.begin(), backgroundData.end());
|
||||||
|
|
||||||
|
// highlited white keys
|
||||||
|
constexpr int WHITE_KEYS_COUNT = 31;
|
||||||
|
static float greenColor = data::GetIntColor(GREEN);
|
||||||
|
for (int i = 0; i < WHITE_KEYS_COUNT; i++) {
|
||||||
|
int touche = GetKeyFromWhite(i);
|
||||||
|
|
||||||
|
if (!IsKeyHighlited(touche))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float x = (float)i / (float)WHITE_KEYS_COUNT;
|
||||||
|
float dx = (float)(i + 1) / (float)WHITE_KEYS_COUNT;
|
||||||
|
float y = 0.0f;
|
||||||
|
float dy = KEYBOARD_HEIGHT;
|
||||||
|
|
||||||
|
VertexData rectData = data::GetRectData(x, y, dx, dy, greenColor);
|
||||||
|
vertexData.insert(vertexData.end(), rectData.begin(), rectData.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// black borders
|
||||||
|
constexpr float BORDER_THIKNESS = 0.001;
|
||||||
|
constexpr int BORDER_COUNT = 31;
|
||||||
|
static float color = data::GetIntColor(BLACK);
|
||||||
|
|
||||||
|
for (int i = 1; i < BORDER_COUNT; i++) {
|
||||||
|
float centerX = (float)(i) / (float)BORDER_COUNT;
|
||||||
|
float x = centerX - BORDER_THIKNESS / 2.0;
|
||||||
|
float dx = centerX + BORDER_THIKNESS / 2.0;
|
||||||
|
|
||||||
|
VertexData rectData = data::GetRectData(x, 0.0f, dx, KEYBOARD_HEIGHT, color);
|
||||||
|
vertexData.insert(vertexData.end(), rectData.begin(), rectData.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// black keys
|
||||||
|
constexpr float KEY_THIKNESS = 0.02;
|
||||||
|
constexpr float KEY_HEIGHT = KEYBOARD_HEIGHT * 5.0 / 8.0;
|
||||||
|
static float blackColor = data::GetIntColor(BLACK);
|
||||||
|
|
||||||
|
for (int i = 1; i < BORDER_COUNT; i++) {
|
||||||
|
if (i % 7 != 2 && i % 7 != 5) {
|
||||||
|
float centerX = (float)(i) / (float)BORDER_COUNT;
|
||||||
|
float x = centerX - KEY_THIKNESS / 2.0;
|
||||||
|
float dx = centerX + KEY_THIKNESS / 2.0;
|
||||||
|
|
||||||
|
VertexData rectData = data::GetRectData(x, KEYBOARD_HEIGHT - KEY_HEIGHT, dx, KEYBOARD_HEIGHT, blackColor);
|
||||||
|
vertexData.insert(vertexData.end(), rectData.begin(), rectData.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// highlited black keys
|
||||||
|
constexpr float KEY_HIGHLIGHT_BORDER = 0.002f;
|
||||||
|
static float darkGreenColor = data::GetIntColor(DARK_GREEN);
|
||||||
|
for (int i = 1; i < BORDER_COUNT; i++) {
|
||||||
|
if (i % 7 != 2 && i % 7 != 5) {
|
||||||
|
int touche = GetKeyFromBlack(i);
|
||||||
|
|
||||||
|
if (!IsKeyHighlited(touche))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float centerX = (float)(i) / (float)BORDER_COUNT;
|
||||||
|
float x = centerX - KEY_THIKNESS / 2.0;
|
||||||
|
float dx = centerX + KEY_THIKNESS / 2.0;
|
||||||
|
|
||||||
|
VertexData rectData = data::GetRectData(x + KEY_HIGHLIGHT_BORDER, KEYBOARD_HEIGHT - KEY_HEIGHT + KEY_HIGHLIGHT_BORDER, dx - KEY_HIGHLIGHT_BORDER, KEYBOARD_HEIGHT, darkGreenColor);
|
||||||
|
vertexData.insert(vertexData.end(), rectData.begin(), rectData.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return vertexData;
|
||||||
|
}
|
||||||
|
|
||||||
|
VertexData GetStringsData() {
|
||||||
|
VertexData vertexData;
|
||||||
|
vertexData.reserve(2048);
|
||||||
|
|
||||||
|
constexpr float TAB_OFFSET = KEYBOARD_HEIGHT;
|
||||||
|
|
||||||
|
constexpr int STRING_COUNT = 6;
|
||||||
|
|
||||||
|
constexpr Color WHITE{ 255, 255, 255 };
|
||||||
|
constexpr Color GREY{ 237, 231, 223 };
|
||||||
|
constexpr Color SILVER{ 176, 160, 148 };
|
||||||
|
constexpr Color BROWN{ 80, 55, 55 };
|
||||||
|
constexpr Color GREEN{ 132, 255, 0 };
|
||||||
|
constexpr Color DARK_GREEN{ 57, 190, 0 };
|
||||||
|
constexpr Color CAPO{ 95, 95, 95 };
|
||||||
|
|
||||||
|
// brown background
|
||||||
|
static const float brownColor = data::GetIntColor(BROWN);
|
||||||
|
VertexData brownBackground = data::GetRectData(0.0f, TAB_OFFSET, 1.0f, TAB_OFFSET + TAB_HEIGHT, brownColor);
|
||||||
|
vertexData.insert(vertexData.end(), brownBackground.begin(), brownBackground.end());
|
||||||
|
|
||||||
|
// the 12 frets
|
||||||
|
constexpr int FRET_COUNT = 12;
|
||||||
|
constexpr float FRET_THIKNESS = 0.01;
|
||||||
|
constexpr float FRET_START = 0.105;
|
||||||
|
|
||||||
|
float silverColor = data::GetIntColor(SILVER);
|
||||||
|
|
||||||
|
for (int i = 0; i < FRET_COUNT; i++) {
|
||||||
|
float centerX = FRET_START;
|
||||||
|
for (int j = 0; j < i; j++) {
|
||||||
|
centerX += FRET_START / std::pow(2, (float)j / 12.0f);
|
||||||
|
}
|
||||||
|
float x = centerX - FRET_THIKNESS / 2;
|
||||||
|
float dx = centerX + FRET_THIKNESS / 2;
|
||||||
|
|
||||||
|
VertexData fretData = data::GetRectData(x, TAB_OFFSET, dx, TAB_OFFSET + TAB_HEIGHT, silverColor);
|
||||||
|
vertexData.insert(vertexData.end(), fretData.begin(), fretData.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// the 6 strings
|
||||||
|
constexpr float STRING_THIKNESS = 0.01;
|
||||||
|
static const float darkGreenColor = data::GetIntColor(DARK_GREEN);
|
||||||
|
static const float greyColor = data::GetIntColor(GREY);
|
||||||
|
|
||||||
|
for (int i = 0; i < STRING_COUNT; i++) {
|
||||||
|
float stringThikness = STRING_THIKNESS / (((float)i / 3.0f) + 1.0f);
|
||||||
|
float centerY = TAB_OFFSET + (float)(i + 1) / (float)(STRING_COUNT + 1) * TAB_HEIGHT;
|
||||||
|
float y = centerY - stringThikness / 2;
|
||||||
|
float dy = centerY + stringThikness / 2;
|
||||||
|
|
||||||
|
VertexData stringData = data::GetRectData(0.0f, y, 1.0f, dy, highlitedStrings[i] == data::EMPTY_TAB ? greyColor : darkGreenColor);
|
||||||
|
vertexData.insert(vertexData.end(), stringData.begin(), stringData.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// drawing circles
|
||||||
|
|
||||||
|
constexpr float circleRadius = 1.0f / (float)(FRET_COUNT + 3.0f) * TAB_HEIGHT - STRING_THIKNESS / 2.0f;
|
||||||
|
|
||||||
|
auto getCircleCenterX = [](int fret) -> float {
|
||||||
|
float circleCenterX = FRET_START;
|
||||||
|
for (int i = 0; i < fret - 2; i++) {
|
||||||
|
circleCenterX += FRET_START / std::pow(2, (float)i / 12.0f);
|
||||||
|
}
|
||||||
|
return circleCenterX + (FRET_START / std::pow(2, (float)((fret)-1.0f) / 12.0f)) / 2.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getCircleCenterY = [](int string) -> float {
|
||||||
|
float centerY = TAB_OFFSET + (float)(string + 1) / (float)(STRING_COUNT + 1) * TAB_HEIGHT;
|
||||||
|
centerY -= (1 / (float)(STRING_COUNT + 1) * TAB_HEIGHT) / 2.0f;
|
||||||
|
return centerY;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::vector<int> circlesX = { 3, 5, 7, 9 };
|
||||||
|
for (int i = 0; i < circlesX.size(); i++) {
|
||||||
|
auto circleData = data::GetCircleData(getCircleCenterX(circlesX[i]), TAB_OFFSET + TAB_HEIGHT / 2.0f, circleRadius, data::GetIntColor(WHITE), 20);
|
||||||
|
vertexData.insert(vertexData.end(), circleData.begin(), circleData.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::vector<int> circlesY = { 1, 5 };
|
||||||
|
for (int i = 0; i < circlesY.size(); i++) {
|
||||||
|
auto circleData = data::GetCircleData(getCircleCenterX(12), getCircleCenterY(circlesY[i]), circleRadius, data::GetIntColor(WHITE), 20);
|
||||||
|
vertexData.insert(vertexData.end(), circleData.begin(), circleData.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// fret highlight
|
||||||
|
float greenColor = data::GetIntColor(GREEN);
|
||||||
|
for (int i = 0; i < STRING_COUNT; i++) {
|
||||||
|
|
||||||
|
if (highlitedStrings[i] == data::EMPTY_TAB || highlitedStrings[i] == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int position = highlitedStrings[i];
|
||||||
|
|
||||||
|
float centerY = TAB_OFFSET + (float)(i + 1) / (float)(STRING_COUNT + 1) * TAB_HEIGHT;
|
||||||
|
float y = centerY - (1.0f / (float)(STRING_COUNT + 1) * TAB_HEIGHT) / 2;
|
||||||
|
float dy = centerY + (1.0f / (float)(STRING_COUNT + 1) * TAB_HEIGHT) / 2;
|
||||||
|
|
||||||
|
float centerX = FRET_START;
|
||||||
|
for (int j = 0; j < position - 1; j++) {
|
||||||
|
centerX += FRET_START / std::pow(2, (float)j / 12.0f);
|
||||||
|
}
|
||||||
|
float x = centerX - FRET_THIKNESS / 2 * 6;
|
||||||
|
float dx = centerX - FRET_THIKNESS / 2;
|
||||||
|
|
||||||
|
VertexData fretData = data::GetRectData(x, y, dx, dy, greenColor);
|
||||||
|
vertexData.insert(vertexData.end(), fretData.begin(), fretData.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw capo
|
||||||
|
static float capoColor = data::GetIntColor(CAPO);
|
||||||
|
constexpr float CAPO_OFFSET = 0.01;
|
||||||
|
|
||||||
|
if (capo != 0) {
|
||||||
|
float centerX = FRET_START;
|
||||||
|
for (int j = 0; j < capo - 1; j++) {
|
||||||
|
centerX += FRET_START / std::pow(2, (float)j / 12.0f);
|
||||||
|
}
|
||||||
|
centerX -= CAPO_OFFSET;
|
||||||
|
float x = centerX - FRET_THIKNESS / 2 * 6;
|
||||||
|
float dx = centerX - FRET_THIKNESS / 2;
|
||||||
|
|
||||||
|
VertexData fretData = data::GetRectData(x, TAB_OFFSET, dx, TAB_OFFSET + TAB_HEIGHT, capoColor);
|
||||||
|
vertexData.insert(vertexData.end(), fretData.begin(), fretData.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
return vertexData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitRendering() {
|
||||||
|
gpShader.LoadProgram(vertexShader, fragmentShader);
|
||||||
|
|
||||||
|
ClearTab();
|
||||||
|
|
||||||
|
keyData = data::GetDrawData(GetKeyboardData());
|
||||||
|
stringsData = data::GetDrawData(GetStringsData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateBuffers() {
|
||||||
|
data::UpdateData(keyData, GetKeyboardData());
|
||||||
|
data::UpdateData(stringsData, GetStringsData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawWidgets() {
|
||||||
|
gpShader.Start();
|
||||||
|
data::DrawVertexData(keyData);
|
||||||
|
data::DrawVertexData(stringsData);
|
||||||
|
gpShader.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace renderer
|
||||||
|
} // namespace gpgui
|
||||||
92
src/GPSave.cpp
Normal file
92
src/GPSave.cpp
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#include "GPSave.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace gpgui {
|
||||||
|
namespace save {
|
||||||
|
|
||||||
|
static constexpr std::uint8_t SAVE_VERSION = 0;
|
||||||
|
|
||||||
|
typedef std::vector<std::uint8_t> DataBuffer;
|
||||||
|
typedef std::uint16_t SongSizeType;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static void WriteData(DataBuffer& buffer, const T* data, std::size_t dataSize = sizeof(T)) {
|
||||||
|
std::size_t endPos = buffer.size();
|
||||||
|
|
||||||
|
buffer.resize(endPos + dataSize);
|
||||||
|
std::memcpy(buffer.data() + endPos, data, dataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteFile(const DataBuffer& buffer, const std::string& fileName) {
|
||||||
|
std::ofstream fileStream(fileName);
|
||||||
|
|
||||||
|
if (!fileStream)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fileStream.write(reinterpret_cast<const char*>(buffer.data()), buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveSongToFile(const Song& song, const std::string& fileName) {
|
||||||
|
DataBuffer buffer;
|
||||||
|
|
||||||
|
WriteData(buffer, &SAVE_VERSION); // writing file version
|
||||||
|
|
||||||
|
WriteData(buffer, &song.capo); // writing the capo offset as 8 bit unsigned integer
|
||||||
|
|
||||||
|
SongSizeType songSize = song.chords.size();
|
||||||
|
WriteData(buffer, &songSize); // writing the size as 16 bit unsigned int
|
||||||
|
|
||||||
|
WriteData(buffer, song.chords.data(), songSize * sizeof(ChordSave));
|
||||||
|
|
||||||
|
WriteFile(buffer, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Song LoadSongVersion0(const std::string& data, std::size_t offset, const std::string& filePath) {
|
||||||
|
Song song{ "", 0 };
|
||||||
|
|
||||||
|
std::memcpy(&song.capo, data.data() + offset, sizeof(song.capo)); // reading capo pos
|
||||||
|
offset += sizeof(song.capo);
|
||||||
|
|
||||||
|
SongSizeType songSize;
|
||||||
|
std::memcpy(&songSize, data.data() + offset, sizeof(songSize)); // reading song size
|
||||||
|
|
||||||
|
offset += sizeof(songSize);
|
||||||
|
|
||||||
|
song.chords.resize(songSize);
|
||||||
|
std::memcpy(song.chords.data(), data.data() + offset, songSize * sizeof(ChordSave)); // reading chords
|
||||||
|
|
||||||
|
song.title = filePath.substr(0, filePath.find_last_of('.'));
|
||||||
|
|
||||||
|
return song;
|
||||||
|
}
|
||||||
|
|
||||||
|
Song LoadSongFromFile(const std::string& filePath) {
|
||||||
|
std::ifstream fileStream(filePath);
|
||||||
|
|
||||||
|
if (!fileStream)
|
||||||
|
return { "", 0 };
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << fileStream.rdbuf();
|
||||||
|
|
||||||
|
std::string str = oss.str();
|
||||||
|
|
||||||
|
std::uint8_t fileVersion;
|
||||||
|
std::memcpy(&fileVersion, str.data(), sizeof(fileVersion)); // reading file save version
|
||||||
|
|
||||||
|
std::size_t offset = sizeof(fileVersion);
|
||||||
|
|
||||||
|
switch (fileVersion) {
|
||||||
|
case 0:
|
||||||
|
return LoadSongVersion0(str, offset, filePath);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return { "", 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace save
|
||||||
|
} // namespace gpgui
|
||||||
91
src/ShaderProgram.cpp
Executable file
91
src/ShaderProgram.cpp
Executable file
@@ -0,0 +1,91 @@
|
|||||||
|
#include "ShaderProgram.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <GL/glew.h>
|
||||||
|
|
||||||
|
namespace gpgui {
|
||||||
|
|
||||||
|
ShaderProgram::ShaderProgram() :
|
||||||
|
m_ProgramID(0), m_VertexShaderID(0), m_FragmentShaderID(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderProgram::~ShaderProgram() {
|
||||||
|
CleanUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderProgram::Start() const {
|
||||||
|
glUseProgram(m_ProgramID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderProgram::Stop() const {
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderProgram::CleanUp() const {
|
||||||
|
Stop();
|
||||||
|
glDetachShader(m_ProgramID, m_VertexShaderID);
|
||||||
|
glDetachShader(m_ProgramID, m_FragmentShaderID);
|
||||||
|
glDeleteShader(m_VertexShaderID);
|
||||||
|
glDeleteShader(m_FragmentShaderID);
|
||||||
|
glDeleteProgram(m_ProgramID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderProgram::LoadProgramFile(const std::string& vertexFile,
|
||||||
|
const std::string& fragmentFile) {
|
||||||
|
m_VertexShaderID = static_cast<unsigned int>(LoadShaderFromFile(vertexFile, static_cast<unsigned int>(GL_VERTEX_SHADER)));
|
||||||
|
m_FragmentShaderID = static_cast<unsigned int>(LoadShaderFromFile(fragmentFile, static_cast<unsigned int>(GL_FRAGMENT_SHADER)));
|
||||||
|
m_ProgramID = glCreateProgram();
|
||||||
|
glAttachShader(m_ProgramID, m_VertexShaderID);
|
||||||
|
glAttachShader(m_ProgramID, m_FragmentShaderID);
|
||||||
|
glLinkProgram(m_ProgramID);
|
||||||
|
glValidateProgram(m_ProgramID);
|
||||||
|
GetAllUniformLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderProgram::LoadProgram(const std::string& vertexSource,
|
||||||
|
const std::string& fragmentSource) {
|
||||||
|
m_VertexShaderID = static_cast<unsigned int>(LoadShader(vertexSource, static_cast<unsigned int>(GL_VERTEX_SHADER)));
|
||||||
|
m_FragmentShaderID = static_cast<unsigned int>(LoadShader(fragmentSource, static_cast<unsigned int>(GL_FRAGMENT_SHADER)));
|
||||||
|
m_ProgramID = glCreateProgram();
|
||||||
|
glAttachShader(m_ProgramID, m_VertexShaderID);
|
||||||
|
glAttachShader(m_ProgramID, m_FragmentShaderID);
|
||||||
|
glLinkProgram(m_ProgramID);
|
||||||
|
glValidateProgram(m_ProgramID);
|
||||||
|
GetAllUniformLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int ShaderProgram::LoadShader(const std::string& source, unsigned int type) {
|
||||||
|
unsigned int shaderID = glCreateShader(GLenum(type));
|
||||||
|
|
||||||
|
const char* c_str = source.c_str();
|
||||||
|
int* null = 0;
|
||||||
|
glShaderSource(shaderID, 1, &c_str, null);
|
||||||
|
glCompileShader(shaderID);
|
||||||
|
GLint compilesuccessful;
|
||||||
|
glGetShaderiv(shaderID, GL_COMPILE_STATUS, &compilesuccessful);
|
||||||
|
if (compilesuccessful == false) {
|
||||||
|
GLsizei size;
|
||||||
|
glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &size);
|
||||||
|
std::vector<char> shaderError(static_cast<std::size_t>(size));
|
||||||
|
glGetShaderInfoLog(shaderID, size, &size, shaderError.data());
|
||||||
|
}
|
||||||
|
return shaderID;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int ShaderProgram::LoadShaderFromFile(const std::string& file, unsigned int type) {
|
||||||
|
std::stringstream stream;
|
||||||
|
std::ifstream fileStream(file);
|
||||||
|
|
||||||
|
if (fileStream) {
|
||||||
|
stream << fileStream.rdbuf();
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LoadShader(stream.str(), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace gpgui
|
||||||
144
src/main.cpp
Normal file
144
src/main.cpp
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
// Dear ImGui: standalone example application for GLFW + OpenGL 3, using programmable pipeline
|
||||||
|
// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.)
|
||||||
|
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||||
|
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "backends/imgui_impl_glfw.h"
|
||||||
|
#include "backends/imgui_impl_opengl3.h"
|
||||||
|
#include <GL/glew.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
#endif
|
||||||
|
#include <GLFW/glfw3.h> // Will drag system OpenGL headers
|
||||||
|
|
||||||
|
// [Win32] Our example includes a copy of glfw3.lib pre-compiled with VS2010 to maximize ease of testing and compatibility with old VS compilers.
|
||||||
|
// To link with VS2010-era libraries, VS2015+ requires linking with legacy_stdio_definitions.lib, which we do using this pragma.
|
||||||
|
// Your own project should not be affected, as you are likely to link with a newer binary of GLFW that is adequate for your version of Visual Studio.
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1900) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
|
||||||
|
#pragma comment(lib, "legacy_stdio_definitions")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "GPGui.h"
|
||||||
|
#include "GPRenderer.h"
|
||||||
|
|
||||||
|
static void glfw_error_callback(int error, const char* description)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Glfw Error %d: %s\n", error, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int, char**)
|
||||||
|
{
|
||||||
|
// Setup window
|
||||||
|
glfwSetErrorCallback(glfw_error_callback);
|
||||||
|
if (!glfwInit())
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// Decide GL+GLSL versions
|
||||||
|
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||||
|
// GL ES 2.0 + GLSL 100
|
||||||
|
const char* glsl_version = "#version 100";
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||||
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
// GL 3.2 + GLSL 150
|
||||||
|
const char* glsl_version = "#version 150";
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||||
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||||
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
|
||||||
|
#else
|
||||||
|
// GL 3.0 + GLSL 130
|
||||||
|
const char* glsl_version = "#version 130";
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
||||||
|
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||||
|
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Create window with graphics context
|
||||||
|
GLFWwindow* window = glfwCreateWindow(1920, 1080, "GuitaroPianoGui", NULL, NULL);
|
||||||
|
if (window == NULL)
|
||||||
|
return 1;
|
||||||
|
glfwMakeContextCurrent(window);
|
||||||
|
glfwSwapInterval(1); // Enable vsync
|
||||||
|
|
||||||
|
// Setup Dear ImGui context
|
||||||
|
IMGUI_CHECKVERSION();
|
||||||
|
ImGui::CreateContext();
|
||||||
|
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||||
|
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||||
|
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||||
|
|
||||||
|
// Setup Dear ImGui style
|
||||||
|
ImGui::StyleColorsDark();
|
||||||
|
//ImGui::StyleColorsClassic();
|
||||||
|
|
||||||
|
// Setup Platform/Renderer backends
|
||||||
|
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
||||||
|
ImGui_ImplOpenGL3_Init(glsl_version);
|
||||||
|
|
||||||
|
// Load Fonts
|
||||||
|
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
|
||||||
|
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
|
||||||
|
// - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
|
||||||
|
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
|
||||||
|
// - Read 'docs/FONTS.md' for more instructions and details.
|
||||||
|
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
|
||||||
|
//io.Fonts->AddFontDefault();
|
||||||
|
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
|
||||||
|
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
|
||||||
|
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
|
||||||
|
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
|
||||||
|
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
|
||||||
|
//IM_ASSERT(font != NULL);
|
||||||
|
|
||||||
|
// Our state
|
||||||
|
bool show_demo_window = true;
|
||||||
|
bool show_another_window = false;
|
||||||
|
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||||
|
|
||||||
|
gpgui::renderer::InitRendering();
|
||||||
|
gpgui::gui::Init();
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
while (!glfwWindowShouldClose(window)) {
|
||||||
|
// Poll and handle events (inputs, window resize, etc.)
|
||||||
|
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
||||||
|
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
|
||||||
|
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
|
||||||
|
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
||||||
|
glfwPollEvents();
|
||||||
|
|
||||||
|
// Start the Dear ImGui frame
|
||||||
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
|
ImGui_ImplGlfw_NewFrame();
|
||||||
|
ImGui::NewFrame();
|
||||||
|
|
||||||
|
// Rendering
|
||||||
|
int display_w, display_h;
|
||||||
|
glfwGetFramebufferSize(window, &display_w, &display_h);
|
||||||
|
glViewport(0, 0, display_w, display_h);
|
||||||
|
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
gpgui::gui::Render();
|
||||||
|
gpgui::renderer::DrawWidgets();
|
||||||
|
ImGui::Render();
|
||||||
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
|
||||||
|
glfwSwapBuffers(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
ImGui_ImplOpenGL3_Shutdown();
|
||||||
|
ImGui_ImplGlfw_Shutdown();
|
||||||
|
ImGui::DestroyContext();
|
||||||
|
|
||||||
|
glfwDestroyWindow(window);
|
||||||
|
glfwTerminate();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
test/Epoch.gp
Normal file
BIN
test/Epoch.gp
Normal file
Binary file not shown.
20
xmake.lua
Normal file
20
xmake.lua
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
add_rules("mode.debug", "mode.release")
|
||||||
|
|
||||||
|
add_requires("opengl", "glfw >= 3", "glew >= 2")
|
||||||
|
|
||||||
|
target("GuitarPianoGui")
|
||||||
|
set_kind("binary")
|
||||||
|
add_files("src/*.cpp")
|
||||||
|
add_includedirs("include")
|
||||||
|
|
||||||
|
add_packages("opengl", "glfw", "glew")
|
||||||
|
|
||||||
|
set_languages("c++17")
|
||||||
|
|
||||||
|
-- ImGui
|
||||||
|
add_includedirs("libs/imgui")
|
||||||
|
add_files("libs/imgui/*.cpp", "libs/imgui/backends/imgui_impl_opengl3.cpp", "libs/imgui/backends/imgui_impl_glfw.cpp")
|
||||||
|
|
||||||
|
if is_os("windows") then
|
||||||
|
add_ldflags("-static")
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user