#pragma once #include #include #include #include #include #include #include "JUI/Widgets/CommandLine.hpp" #include "JGL/types/Shader.h" #include #include #include "JUI/Widgets/UtilityBar.hpp" #include #include "JUI/Widgets/Image.hpp" #include "JUI/Widgets/ImageRect.hpp" #include #pragma region OpenGL Introspection inline std::string GetGLVendor() { return {(const char*)glGetString(GL_VENDOR)}; } inline std::string GetGLRenderer() { return {(const char*)glGetString(GL_RENDERER)}; } inline std::string GetGLVersion() { return {(const char*)glGetString(GL_VERSION)}; } inline std::string GetGLSLVersion() { return {(const char*)glGetString(GL_SHADING_LANGUAGE_VERSION)}; } inline std::string GetGLExtensions() { return {(const char*)glGetString(GL_EXTENSIONS)}; } inline std::vector GetGLExtensionList() { return misc::string_expand({(const char*)glGetString(GL_EXTENSIONS)}); } #pragma endregion using namespace JUI::UDimLiterals; enum class Fractal { MandelbrotSet, JuliaSet }; template struct Lerped { T current; T goal; float rate = 1.f; void Update(float elapsed); }; template <> inline void Lerped::Update(float elapsed) { current = Math::Lerp(current, goal, elapsed*rate); } template <> inline void Lerped::Update(float elapsed) { current = Vector2::Lerp(current, goal, elapsed*rate); } /// This class represents the FractalApp program state. class FractalInspectorApp : public ReWindow::OpenGLWindow { protected: JUI::Scene *scene; JUI::UtilityBar* toolbar; JUI::CommandLine *console; JUI::Window* info_dialog; JUI::TextRect* fps_label; JUI::Window* mandelbrotset_dialog; JUI::Window* juliaset_dialog; JUI::Window* colorpicker_window; JGL::Shader mandelbrot_shader; JGL::Shader julia_shader; JGL::RenderTarget *canvas; JUI::Slider* julia_x_slider; JUI::Slider* julia_y_slider; JUI::Text* julia_x_label; JUI::Text* julia_y_label; float u_time; float u_scale = 2.f; float u_scale_goal = 2.f; Vector2 u_translation {0.4f, 0.f}; Vector2 u_translation_goal {0.4f, 0.f}; Color4 u_rgb_1 = Colors::Greens::Chartreuse; Color4 u_rgb_2 = Colors::Reds::LightCoral; Color4 u_rgb_3 = Colors::Yellow; Color4 u_rgb_4 = Colors::Green; Vector2 u_julia_set {0,0}; Vector2 u_julia_value {0,0}; bool panning = false; Fractal current_fractal = Fractal::MandelbrotSet; void OnShaderLoadFail(const std::string&, const std::string&); public: FractalInspectorApp(int width, int height); /// Returns a full-path file name for a GLSL vertex shader, from a given prefix name. static std::filesystem::path VertexShaderFilepathFromPrefixName(const std::string &name); /// Returns a full-path file name for a GLSL fragment shader, from a given prefix name. static std::filesystem::path FragmentShaderFilepathFromPrefixName(const std::string &name); void LoadShaders(); void ReloadShader(); using CmdArgs = std::vector; void PositionCmd(const CmdArgs& args); void ZoomCmd(const CmdArgs &args); void ParseCmdLineMessage(const std::string& message); /// Loads the Julia set program -- consists of a shader program and special widget for parameters. void LoadJulia(); /// Unloads the Julia set program. /// @see LoadJulia void UnloadJulia(); /// Creates and returns the "Info" dialog for the program. Think of it like a watermark. JUI::Window* CreateAppInfoDialogWindow(JUI::Widget* parent); /// Creates and styles the program's GUI. void CreateMenu(); /// Initializes the ReWindow and sets up program state, GUI widgets, etc. bool Open() override; /// Passes the current window size to subordinate objects, such as the JUI scene. void PropagateWindowSize(); Vector2 FractalSpaceToScreenSpace(const Vector2& pos) { Vector2 R(GetWidth(), GetHeight()); Vector2 translation(u_translation.x, -u_translation.y); return ((pos + u_translation) * R.y + R + Vector2(1.f, 1.f)) / u_scale; } Vector2 ScreenSpaceToFractalSpace(const Vector2& pos) { Vector2 tpos = -pos; Vector2 R(GetWidth(), GetHeight()); Vector2 scaled = pos * u_scale; Vector2 translation(u_translation.x, -u_translation.y); Vector2 uv = ((scaled - R - Vector2(1.f, 1.f)) / R.y) - u_translation; return uv; } void ZoomIn(float rate); void ZoomInTowards(const Vector2 &dir, float rate); void ZoomOutTowards(const Vector2 &dir, float rate); void ZoomOut(float rate); /// Performs a logic update. void Update(float elapsed); float ReadableScale(); /// Pushes updated shader paramters to the OpenGL shader program. void UpdateShaderUniforms(float elapsed); /// Renders the program. void Draw(); /// Called by the ReWindow base, updates and then re-draws the program. void OnRefresh(float elapsed) override; /// Called by the ReWindow base when the user presses a mouse button. void OnMouseButtonDown(const ReWindow::MouseButtonDownEvent &e) override; /// Called by the ReWindow base when the user releases a mouse button. void OnMouseButtonUp(const ReWindow::MouseButtonUpEvent &e) override; /// Called by the ReWindow base when the user moves the mouse / pointer device. void OnMouseMove(const ReWindow::MouseMoveEvent &e) override; /// Called by the ReWindow base when the user presses a key. void OnKeyDown(const ReWindow::KeyDownEvent &e) override; /// Called by the ReWindow base when the user presses a key. void OnKeyUp(const ReWindow::KeyUpEvent &e) override; void OnMouseWheel(const ReWindow::MouseWheelEvent &e) override { // Test of zooming in toward the mouse. auto mouse_pos_ipair = GetMouseCoordinates(); auto resolution_ipair = GetSize(); auto mouse_pos = Vector2(mouse_pos_ipair.x, mouse_pos_ipair.y); auto resolution = Vector2(resolution_ipair.x, resolution_ipair.y); auto mp_norm = mouse_pos / resolution; mp_norm -= {.5f, .5f}; mp_norm *= 2.f; mp_norm = {-mp_norm.x, mp_norm.y}; if (e.WheelMovement > 0) { ZoomOutTowards(mp_norm, 0.1f); } if (e.WheelMovement < 0) { ZoomInTowards(mp_norm, 0.1f); } } void TakeScreenshot(); protected: private: };