diff --git a/include/FractalApp.hpp b/include/FractalApp.hpp index c7d5eba..ee16360 100644 --- a/include/FractalApp.hpp +++ b/include/FractalApp.hpp @@ -35,6 +35,7 @@ enum class Fractal { MandelbrotSet, JuliaSet }; +/// This class represents the FractalApp program state. class FractalInspectorApp : public ReWindow::OpenGLWindow { protected: JUI::Scene *scene; @@ -59,10 +60,13 @@ protected: 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(); + 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); @@ -101,6 +105,8 @@ public: /// Performs a logic update. void Update(float elapsed); + float ReadableScale(); + /// Pushes updated shader paramters to the OpenGL shader program. void UpdateShaderUniforms(float elapsed); diff --git a/main.cpp b/main.cpp index 0ebe896..af7c46f 100644 --- a/main.cpp +++ b/main.cpp @@ -11,7 +11,12 @@ int main(int argc, char** argv) { ReWindow::Logger::Debug.EnableConsole(false); - auto* program = new FractalInspectorApp(); + int default_app_width = 1080; + int default_app_height = 768; + + // TODO: Create AppConfig struct and pass to constructor. + + auto* program = new FractalInspectorApp(default_app_width, default_app_height); program->Open(); program->SetFullscreen(false); diff --git a/shaders/mandelbrot.frag.glsl b/shaders/mandelbrot.frag.glsl index 9c8316d..08b5067 100644 --- a/shaders/mandelbrot.frag.glsl +++ b/shaders/mandelbrot.frag.glsl @@ -68,6 +68,9 @@ float iterate_mandelbrot(vec2 p) { void main() { vec2 R = u_resolution.xy; + // Unused as of yet, see TODOs for what and why. + float scale = (3.0 - u_scale); + vec2 uv = (u_scale * gl_FragCoord.xy - R - 1.) / R.y - u_translation; float n = iterate_mandelbrot(uv) / N; diff --git a/src/FractalApp.cpp b/src/FractalApp.cpp index 30d6d7f..6b848f5 100644 --- a/src/FractalApp.cpp +++ b/src/FractalApp.cpp @@ -1,15 +1,21 @@ #include -FractalInspectorApp::FractalInspectorApp(): ReWindow::OpenGLWindow("ReShader", 1800, 1000, 2, 1) { - Shader::OnCompilationErrorMessage += [this](std::string type, std::string infoLog) { - auto log_lines = misc::string_expand(infoLog, '\n'); - console->Log(type, Colors::Red); - std::cerr << type << std::endl; - for (auto line: log_lines) { - console->Log(line, Colors::Red); - std::cerr << line << std::endl; - } - }; +#define GL_VER_MAJOR 2 +#define GL_VER_MINOR 1 + +void FractalInspectorApp::OnShaderLoadFail(const std::string& type, const std::string& infoLog) { + auto log_lines = misc::string_expand(infoLog, '\n'); + console->Log(type, Colors::Red); + std::cerr << type << std::endl; + for (auto line: log_lines) { + console->Log(line, Colors::Red); + std::cerr << line << std::endl; + } +} + +FractalInspectorApp::FractalInspectorApp(int width, int height): + OpenGLWindow("FractalInspector",width, height, GL_VER_MAJOR, GL_VER_MINOR) { + Shader::OnCompilationErrorMessage += [this] (auto... args) { OnShaderLoadFail(args...);}; u_time = 0; } @@ -439,11 +445,20 @@ void FractalInspectorApp::Update(float elapsed) { int fps = 1.f / elapsed; - std::string fps_text = std::format("FPS: {}", fps); + + // TODO: Consider moving this logic deeper into the shader to make it easier to work with. + float readable_scale = Math::Round(ReadableScale(), 3); + + std::string fps_text = std::format("Pos: {},{} Zoom: {}x FPS: {}", + Math::Round(u_translation.x, 2), Math::Round(u_translation.y, 2), readable_scale, fps); fps_label->SetContent(fps_text); } +float FractalInspectorApp::ReadableScale() { + return 3.f - u_scale; +} + void FractalInspectorApp::UpdateShaderUniforms(float elapsed) { Vector2 u_res = Vector2(GetSize().x, GetSize().y); @@ -507,27 +522,71 @@ void FractalInspectorApp::OnRefresh(float elapsed) { this->SwapBuffers(); } +enum JUI::MouseButton ToJUIEnum(const MouseButton& btn) { + + if (btn == MouseButtons::Left) return JUI::MouseButton::Left; + if (btn == MouseButtons::Middle) return JUI::MouseButton::Middle; + if (btn == MouseButtons::Right) return JUI::MouseButton::Right; + + // Default condition. + return JUI::MouseButton::Left; +} + void FractalInspectorApp::OnMouseButtonDown(const ReWindow::MouseButtonDownEvent &e) { - if (e.Button == MouseButtons::Left) + + auto btn = ToJUIEnum(e.Button); + + if (scene->ObserveMouseInput(btn, true)) + return; + + if (btn == JUI::MouseButton::Right) + panning = true; + + + /*if (e.Button == MouseButtons::Left) scene->ObserveMouseInput(JUI::MouseButton::Left, true); if (e.Button == MouseButtons::Middle) scene->ObserveMouseInput(JUI::MouseButton::Middle, true); if (e.Button == MouseButtons::Right) - scene->ObserveMouseInput(JUI::MouseButton::Right, true); + scene->ObserveMouseInput(JUI::MouseButton::Right, true);*/ } void FractalInspectorApp::OnMouseButtonUp(const ReWindow::MouseButtonUpEvent &e) { - if (e.Button == MouseButtons::Left) + + + auto btn = ToJUIEnum(e.Button); + + if (scene->ObserveMouseInput(btn, true)) + return; + + if (btn == JUI::MouseButton::Right) + panning = false; + + /*if (e.Button == MouseButtons::Left) scene->ObserveMouseInput(JUI::MouseButton::Left, false); if (e.Button == MouseButtons::Middle) scene->ObserveMouseInput(JUI::MouseButton::Middle, false); if (e.Button == MouseButtons::Right) - scene->ObserveMouseInput(JUI::MouseButton::Right, false); + scene->ObserveMouseInput(JUI::MouseButton::Right, false);*/ } +Vector2 last{0,0}; + void FractalInspectorApp::OnMouseMove(const ReWindow::MouseMoveEvent &e) { Vector2 nmp = Vector2(e.Position.x, e.Position.y); - scene->ObserveMouseMovement(nmp); + if (scene->ObserveMouseMovement(nmp)) + return; + + // TODO: e.Delta is always 0, investigate in JUI. + Vector2 delta = nmp - last; + + float bias_slowdown_as_we_zoom_in = 1.125f; + float base_slowdown = 0.005; + + if (panning) + u_translation += delta*(1.f / ReadableScale()*bias_slowdown_as_we_zoom_in)*base_slowdown; + + last = nmp; } void FractalInspectorApp::OnKeyDown(const ReWindow::KeyDownEvent &e) {