7 Commits

6 changed files with 164 additions and 66 deletions

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.18..3.30)
cmake_minimum_required(VERSION 3.18..3.29)
project(FractalInspector
VERSION 1.0
VERSION 1.1
LANGUAGES CXX)
if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
@@ -15,7 +15,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(cmake/CPM.cmake)
CPMAddPackage(NAME mcolor
URL https://git.redacted.cc/maxine/mcolor/archive/Prerelease-7.3.zip)
URL https://git.redacted.cc/maxine/mcolor/archive/Release-1.zip)
CPMAddPackage(NAME jlog
URL https://git.redacted.cc/josh/jlog/archive/Prerelease-18.zip)
@@ -30,19 +30,25 @@ CPMAddPackage(NAME ReWindow
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-32.zip)
CPMAddPackage(NAME JGL
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-55.zip)
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-58.zip)
CPMAddPackage(NAME JUI
URL https://git.redacted.cc/josh/ReJUI/archive/Prerelease-6.zip)
URL https://git.redacted.cc/josh/ReJUI/archive/Prerelease-6.4.zip)
#file(COPY "assets" DESTINATION "${PROJECT_BINARY_DIR}")
file(COPY "shaders" DESTINATION "${PROJECT_BINARY_DIR}")
#file(GLOB_RECURSE ASSETS "assets/")
file(GLOB_RECURSE HEADERS "include/*.h" "include/*.hpp")
file(GLOB_RECURSE SOURCES "src/*.c" "src/*.cpp")
include_directories(${PROJECT_SOURCE_DIR}/include)
add_executable(FractalInspector main.cpp ${SOURCES})
if (WIN32)
add_executable(FractalInspector main.cpp ${SOURCES} app.rc)
endif()
if (UNIX)
add_executable(FractalInspector main.cpp ${SOURCES})
endif()
target_include_directories(FractalInspector PUBLIC ${Event_SOURCE_DIR}/include)
target_include_directories(FractalInspector PUBLIC ${J3ML_SOURCE_DIR}/include)

4
app.rc Normal file
View File

@@ -0,0 +1,4 @@
MAINICON ICON "icon.ico"
IDI_ICON1 ICON DISCARDABLE "icon.ico"
MANIFEST ICON "icon.ico"
AAA ICON "icon.ico"

View File

@@ -54,19 +54,19 @@ inline void Lerped<Vector2>::Update(float elapsed) { current = Vector2::Lerp(cur
/// 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::Scene *scene = nullptr;
JUI::UtilityBar* toolbar = nullptr;
JUI::CommandLine *console = nullptr;
JUI::Window* info_dialog = nullptr;
JUI::TextRect* fps_label = nullptr;
JUI::Window* mandelbrotset_dialog;
JUI::Window* juliaset_dialog;
JUI::Window* colorpicker_window;
JUI::Window* mandelbrotset_dialog = nullptr;
JUI::Window* juliaset_dialog = nullptr;
JUI::Window* colorpicker_window = nullptr;
JGL::Shader mandelbrot_shader;
JGL::Shader julia_shader;
JGL::RenderTarget *canvas;
JGL::Shader* mandelbrot_shader = nullptr;
JGL::Shader* julia_shader = nullptr;
JGL::RenderTarget *canvas = nullptr;
float u_time;
float u_scale = 2.f;
float u_scale_goal = 2.f;
@@ -81,6 +81,7 @@ protected:
bool panning = false;
Fractal current_fractal = Fractal::MandelbrotSet;
JGL::Shader* current_shader;
void OnShaderLoadFail(const std::string&, const std::string&);
public:
@@ -122,6 +123,23 @@ public:
/// 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);

View File

@@ -8,9 +8,10 @@
// TODO: Parse command-line args.
int main(int argc, char** argv) {
mcolor::windowsSaneify();
ReWindow::Logger::Debug.EnableConsole(false);
int default_app_width = 1660;
int default_app_width = 1440;
int default_app_height = 900;
// TODO: Create AppConfig struct and pass to constructor.

View File

@@ -24,7 +24,7 @@ uniform vec3 u_rgb_3;
uniform vec3 u_rgb_4;
#define N 256. // Number of iterations?
#define B 4. // What does B mean?
#define B 8. // What does B mean?
// The mandelbrot set is a set of complex numbers c for which the function:
// f(z) = z*z + c
@@ -38,8 +38,8 @@ float iterate_mandelbrot(vec2 p) {
for (i=0.; i < N; i++ ) {
// This line performs the core Mandelbrot iteration: z = z^2 + c.
// It is done using a matrix multiplication to perform the complex number squaring.
// If z = x + iy, then z&2 = (i+iy)(x+iy) = x^2 - y ^ 2 + 2ixy.
// The matrix [x, -y; y, x] multiplied by [x, y] gives [x*x - y&y, y*x + x*y] = [Re(z^2) Im(z^2)].
// If z = x + iy, then z&2 = (x+iy)(x+iy) = x^2 - y ^ 2 + 2ixy.
// The matrix [x, -y; y, x] multiplied by [x, y] gives [x*x - y*y, y*x + x*y] = [Re(z^2) Im(z^2)].
// Then we add the constant complex number c (represented by the input vec2).
z = mat2(z, -z.y, z.x) * z + c;

View File

@@ -20,19 +20,19 @@ FractalInspectorApp::FractalInspectorApp(int width, int height):
}
std::filesystem::path FractalInspectorApp::VertexShaderFilepathFromPrefixName(const std::string &name) {
return "../shaders/" + name + ".vert.glsl";
return "shaders/" + name + ".vert.glsl";
}
std::filesystem::path FractalInspectorApp::FragmentShaderFilepathFromPrefixName(const std::string &name) {
return "../shaders/" + name + ".frag.glsl";
return "shaders/" + name + ".frag.glsl";
}
void FractalInspectorApp::LoadShaders() {
// TODO: Come up with a decent way to bake the shader program sources into the executable, when compiling a release build!
mandelbrot_shader = Shader(VertexShaderFilepathFromPrefixName("test"), FragmentShaderFilepathFromPrefixName("mandelbrot"));
julia_shader = Shader(VertexShaderFilepathFromPrefixName("test" ), FragmentShaderFilepathFromPrefixName("julia"));
mandelbrot_shader = new Shader(VertexShaderFilepathFromPrefixName("test"), FragmentShaderFilepathFromPrefixName("mandelbrot"));
julia_shader = new Shader(VertexShaderFilepathFromPrefixName("test" ), FragmentShaderFilepathFromPrefixName("julia"));
}
void FractalInspectorApp::ReloadShader() {
@@ -105,13 +105,25 @@ void FractalInspectorApp::UnloadJulia() {
juliaset_dialog->Close();
}
// Tested and works on both platform, but is a security **NIGHTMARE**.
void OpenURL(const std::string &url) {
#ifdef _WIN32
system(std::format("start {}", url).c_str());
#endif
#ifdef linux
system(std::format("xdg-open {}", url).c_str());
#endif
}
JUI::Window * FractalInspectorApp::CreateAppInfoDialogWindow(JUI::Widget *parent) {
// TODO: Implement JUI structure that makes blocks of text easy to impelement.
auto window = new JUI::Window(parent);
window->SetTitle("About FractalInspector");
window->Title("About FractalInspector");
window->Size({300_px, 375_px});
window->MinSize({300, 375});
window->Position({100_percent - 325_px, 100_percent - 400_px});
auto* layout = new JUI::VerticalListLayout(window->Content());
layout->Padding(0_px);
@@ -119,9 +131,9 @@ JUI::Window * FractalInspectorApp::CreateAppInfoDialogWindow(JUI::Widget *parent
auto line_item = [&] (const std::string& text, int size = 20, const Color4& color = Colors::White) {
auto* content = new JUI::TextRect(layout);
content->Size({100_percent, JUI::UDim(size+5, 0)});
content->SetContent(text);
content->SetTextSize(size);
content->SetTextColor(color);
content->Content(text);
content->TextSize(size);
content->TextColor(color);
content->BGColor(Colors::Transparent);
content->AlignCenterHorizontally();
content->BorderWidth(0);
@@ -156,18 +168,23 @@ JUI::Window * FractalInspectorApp::CreateAppInfoDialogWindow(JUI::Widget *parent
auto* btn_layout = new JUI::HorizontalListLayout(btn_box);
btn_layout->Padding(8_px);
auto btn_item = [&] (const std::string& text) -> JUI::TextButton* {
auto btn_item = [&] (const std::string& text, const std::function<void()>& callback = {}) -> JUI::TextButton* {
JUI::TextButton* btn = new JUI::TextButton(btn_layout);
btn->SetContent(text);
btn->Content(text);
btn->Size({32_percent, 100_percent});
btn->SetTextColor(Colors::Black);
btn->TextColor(Colors::Black);
btn->Center();
btn->OnClickEvent += [callback] (auto a, auto b) {
callback();
};
return btn;
};
auto* btn_a = btn_item("License");
auto* btn_b = btn_item("Wiki");
auto* btn_c = btn_item("Source");
auto* btn_c = btn_item("Source", []() {
OpenURL("https://git.redacted.cc/josh/FractalInspector");
});
return window;
@@ -182,10 +199,12 @@ void FractalInspectorApp::CreateMenu() {
fractals_list->AddButton("Mandelbrot set", [this]{
UnloadJulia();
current_fractal = Fractal::MandelbrotSet;
current_shader = mandelbrot_shader;
});
fractals_list->AddButton("Julia set", [this]{
LoadJulia();
current_fractal = Fractal::JuliaSet;
current_shader = julia_shader;
});
@@ -233,14 +252,14 @@ void FractalInspectorApp::CreateMenu() {
fps_label = new JUI::TextRect(toolbar);
fps_label->Position({100_percent - 200_px, 0_px});
fps_label->Size({200_px, 100_percent});
fps_label->SetTextColor(Colors::Black);
fps_label->SetContent("60 FPS");
fps_label->TextColor(Colors::Black);
fps_label->Content("60 FPS");
// TODO: Utilize for things later.
colorpicker_window = new JUI::Window(scene);
colorpicker_window->SetTitle("Color-Pickers");
colorpicker_window->MinSize({600, 150});
colorpicker_window->Size({600_px, 150_px});
colorpicker_window->Title("Color-Pickers");
colorpicker_window->MinSize({300, 150});
colorpicker_window->Size({400_px, 150_px});
colorpicker_window->Position({50_px, 50_px});
auto* col_layout = new JUI::HorizontalListLayout(colorpicker_window->Content());
@@ -285,6 +304,7 @@ void FractalInspectorApp::CreateMenu() {
console->OnInput += [this] (const std::string& message) {
ParseCmdLineMessage(message);
};
console->Close();
mandelbrotset_dialog = new JUI::Window(scene);
@@ -300,9 +320,9 @@ void FractalInspectorApp::CreateMenu() {
auto* content = new JUI::TextRect(mandelbrotset_layout);
content->Size({100_percent, 25_px});
content->SetContent(text);
content->SetTextSize(size);
content->SetTextColor(color);
content->Content(text);
content->TextSize(size);
content->TextColor(color);
content->BGColor(Colors::Transparent);
content->AlignCenterHorizontally();
content->LayoutOrder(idx--);
@@ -335,7 +355,7 @@ void FractalInspectorApp::CreateMenu() {
juliaset_dialog = new JUI::Window(scene);
juliaset_dialog->SetTitle("Julia Set Inputs");
juliaset_dialog->Title("Julia Set Inputs");
juliaset_dialog->Position({20_px, 300_px});
juliaset_dialog->Size({900_px, 70_px});
juliaset_dialog->Close(); // Defaults to close.
@@ -522,7 +542,10 @@ void FractalInspectorApp::Update(float elapsed) {
std::string fps_text = std::format("Pos: {},{} Zoom: {}x FPS: {}",
Math::Round(u_translation.x, 4), Math::Round(u_translation.y, 4), readable_scale, fps);
fps_label->SetContent(fps_text);
fps_label->Content(fps_text);
}
float FractalInspectorApp::ReadableScale() {
@@ -535,30 +558,41 @@ void FractalInspectorApp::UpdateShaderUniforms(float elapsed) {
// TODO: Why do we need to multiply X by 1.5 here?
Vector2 u_mouse = Vector2(GetMouseCoordinates().x*1.5f, GetSize().y-GetMouseCoordinates().y);
JGL::Shader shader;
;
if (current_fractal == Fractal::JuliaSet) {
shader = julia_shader;
shader.SetVector2("u_julia_set", u_julia_value);
current_shader = julia_shader;
julia_shader->SetVector2("u_julia_set", u_julia_value);
}
if (current_fractal == Fractal::MandelbrotSet) {
shader = mandelbrot_shader;
current_shader = mandelbrot_shader;
}
shader.SetVector2("u_resolution", u_res);
shader.SetFloat("u_time", u_time);
shader.SetVector2("u_mouse", u_mouse);
shader.SetVector3("u_rgb_1", Vector3(u_rgb_1.RN(), u_rgb_1.GN(), u_rgb_1.BN()));
shader.SetVector3("u_rgb_2", Vector3(u_rgb_2.RN(), u_rgb_2.GN(), u_rgb_2.BN()));
shader.SetVector3("u_rgb_3", Vector3(u_rgb_3.RN(), u_rgb_3.GN(), u_rgb_3.BN()));
shader.SetVector3("u_rgb_4", Vector3(u_rgb_4.RN(), u_rgb_4.GN(), u_rgb_4.BN()));
shader.SetFloat("u_scale", u_scale);
shader.SetVector2("u_translation", u_translation);
current_shader->SetVector2("u_resolution", u_res);
current_shader->SetFloat("u_time", u_time);
current_shader->SetVector2("u_mouse", u_mouse);
current_shader->SetVector3("u_rgb_1", Vector3(u_rgb_1.RN(), u_rgb_1.GN(), u_rgb_1.BN()));
current_shader->SetVector3("u_rgb_2", Vector3(u_rgb_2.RN(), u_rgb_2.GN(), u_rgb_2.BN()));
current_shader->SetVector3("u_rgb_3", Vector3(u_rgb_3.RN(), u_rgb_3.GN(), u_rgb_3.BN()));
current_shader->SetVector3("u_rgb_4", Vector3(u_rgb_4.RN(), u_rgb_4.GN(), u_rgb_4.BN()));
current_shader->SetFloat("u_scale", u_scale);
current_shader->SetVector2("u_translation", u_translation);
}
Vector2 solve(Vector2 a, Vector2 b) {
float real = (a.x*a.x) + (-1 * a.y*a.y);
float imag = (a.x*a.y)*2;
real += b.x;
imag += b.y;
return {real, imag};
}
void FractalInspectorApp::Draw() {
Shader::UseDefault();
//Shader::SetDefault(); // TODO: Was this deprecated or replaced?
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
@@ -570,17 +604,52 @@ void FractalInspectorApp::Draw() {
J2D::DrawRenderTarget(canvas, {0, 0});
J2D::End();
J2D::Begin();
const float N = 512.f;
const float B = 2.f;
auto mpos_ipair = GetMouseCoordinates();
Vector2 mpos (mpos_ipair.x, mpos_ipair.y);
Vector2 R(GetWidth(), GetHeight());
Vector2 trans = Vector2(0, R.y) - u_translation;
Vector2 z = Vector2(0, 0);
Vector2 c = ScreenSpaceToFractalSpace(mpos);
Vector2 last_z = c;
J2D::FillCircle(Colors::Red, FractalSpaceToScreenSpace(ScreenSpaceToFractalSpace(mpos)), 6);
for (float i = 0.f; i < N; i++) {
float x = z.x;
float y = z.y;
Vector2 z_nplus1 = Vector2(x*x - y*y, y*x + x*y) + c;
J2D::DrawLine(Colors::White, FractalSpaceToScreenSpace(z), FractalSpaceToScreenSpace(last_z), 0.25);
last_z = z;
z = z_nplus1;
if (Vector2::Dot(z, z) > B*B) break;
}
//
J2D::End();
scene->Draw();
JGL::J2D::Begin(canvas, true);
if (current_fractal == Fractal::JuliaSet) {
julia_shader.Use();
}
if (current_fractal == Fractal::MandelbrotSet) {
mandelbrot_shader.Use();
}
JGL::J2D::FillRect(Colors::Black, {0, 0}, Vector2(GetSize().x, GetSize().y));
JGL::J2D::Begin(canvas, current_shader, true);
JGL::J2D::FillRect(Colors::White, {0, 0}, Vector2(GetSize().x, GetSize().y));
JGL::J2D::End();
}