Initial Commit
This commit is contained in:
49
CMakeLists.txt
Normal file
49
CMakeLists.txt
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.30)
|
||||||
|
|
||||||
|
project(FractalInspector
|
||||||
|
VERSION 1.0
|
||||||
|
LANGUAGES CXX)
|
||||||
|
|
||||||
|
if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
|
||||||
|
message(FATAL_ERROR "In-source builds are not allowed!")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
|
include(cmake/CPM.cmake)
|
||||||
|
|
||||||
|
CPMAddPackage(NAME jlog
|
||||||
|
URL https://git.redacted.cc/josh/jlog/archive/Prerelease-18.zip)
|
||||||
|
|
||||||
|
CPMAddPackage(NAME mcolor
|
||||||
|
URL https://git.redacted.cc/maxine/mcolor/archive/Prerelease-7.2.zip)
|
||||||
|
|
||||||
|
CPMAddPackage(NAME Event
|
||||||
|
URL https://git.redacted.cc/josh/Event/archive/Release-12.zip)
|
||||||
|
|
||||||
|
CPMAddPackage(NAME J3ML
|
||||||
|
URL https://git.redacted.cc/josh/j3ml/archive/3.4.5.zip)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
CPMAddPackage(NAME JUI
|
||||||
|
URL https://git.redacted.cc/josh/ReJUI/archive/Prerelease-5.17.zip)
|
||||||
|
|
||||||
|
add_executable(FractalInspector main.cpp)
|
||||||
|
|
||||||
|
target_include_directories(FractalInspector PUBLIC ${Event_SOURCE_DIR}/include)
|
||||||
|
target_include_directories(FractalInspector PUBLIC ${J3ML_SOURCE_DIR}/include)
|
||||||
|
target_include_directories(FractalInspector PUBLIC ${jlog_SOURCE_DIR}/include)
|
||||||
|
target_include_directories(FractalInspector PUBLIC ${JGL_SOURCE_DIR}/include)
|
||||||
|
target_include_directories(FractalInspector PUBLIC ${ReWindow_SOURCE_DIR}/include)
|
||||||
|
target_include_directories(FractalInspector PUBLIC ${JUI_SOURCE_DIR}/include)
|
||||||
|
target_include_directories(FractalInspector PUBLIC ${mcolor_SOURCE_DIR}/include)
|
||||||
|
|
||||||
|
target_link_libraries(FractalInspector PUBLIC Event J3ML jlog ReWindow JGL JUI mcolor)
|
||||||
|
|
24
cmake/CPM.cmake
Normal file
24
cmake/CPM.cmake
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
|
||||||
|
|
||||||
|
set(CPM_DOWNLOAD_VERSION 0.38.7)
|
||||||
|
set(CPM_HASH_SUM "83e5eb71b2bbb8b1f2ad38f1950287a057624e385c238f6087f94cdfc44af9c5")
|
||||||
|
|
||||||
|
if(CPM_SOURCE_CACHE)
|
||||||
|
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||||
|
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
|
||||||
|
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||||
|
else()
|
||||||
|
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Expand relative path. This is important if the provided path contains a tilde (~)
|
||||||
|
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
|
||||||
|
|
||||||
|
file(DOWNLOAD
|
||||||
|
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
|
||||||
|
${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM}
|
||||||
|
)
|
||||||
|
|
||||||
|
include(${CPM_DOWNLOAD_LOCATION})
|
520
main.cpp
Normal file
520
main.cpp
Normal file
@@ -0,0 +1,520 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <JGL/logger/logger.h>
|
||||||
|
#include <ReWindow/types/Window.h>
|
||||||
|
#include <JUI/Widgets/Scene.hpp>
|
||||||
|
#include <JUI/Widgets/Slider.hpp>
|
||||||
|
#include <JUI/Widgets/Window.hpp>
|
||||||
|
#include "JUI/Widgets/CommandLine.hpp"
|
||||||
|
#include "JGL/types/Shader.h"
|
||||||
|
#include <ReWindow/Logger.h>
|
||||||
|
#include <Color4.hpp>
|
||||||
|
|
||||||
|
#include "JUI/Widgets/UtilityBar.hpp"
|
||||||
|
|
||||||
|
std::vector<std::string> string_expand(const std::string& input, char delimiter = ' ');
|
||||||
|
|
||||||
|
|
||||||
|
#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<std::string> GetGLExtensionList() { return string_expand({(const char*)glGetString(GL_EXTENSIONS)}); }
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
std::vector<std::string> string_expand(const std::string& input, char delimiter)
|
||||||
|
{
|
||||||
|
std::vector<std::string> result;
|
||||||
|
std::stringstream ss (input);
|
||||||
|
std::string item;
|
||||||
|
|
||||||
|
while (getline(ss, item, delimiter)) {
|
||||||
|
result.push_back(item);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace JUI::UDimLiterals;
|
||||||
|
|
||||||
|
/// A JUI Widget that displays a HSV color input dialog.
|
||||||
|
class ColorPicker : public JUI::Rect {
|
||||||
|
public:
|
||||||
|
Event<Color4> OnColorValueChanged;
|
||||||
|
void SetColorValue(const Color4& color);
|
||||||
|
Color4 GetColorValue() const;
|
||||||
|
|
||||||
|
JUI::TextRect* hex_label;
|
||||||
|
|
||||||
|
|
||||||
|
float hue = 0;
|
||||||
|
float sat = 1.f;
|
||||||
|
float bri = 1.f; // AKA val
|
||||||
|
|
||||||
|
ColorPicker() : Rect() {
|
||||||
|
|
||||||
|
Size({100_percent, 100_percent});
|
||||||
|
auto* row_layout = new JUI::VerticalListLayout(this);
|
||||||
|
row_layout->Padding(5_px);
|
||||||
|
|
||||||
|
hue_slider = new JUI::Slider(row_layout);
|
||||||
|
hue_slider->Size({100_percent, 28_px});
|
||||||
|
hue_slider->Minimum(0.f); hue_slider->Maximum(1.f);
|
||||||
|
hue_slider->Interval(1e-3f);
|
||||||
|
hue_slider->ValueChanged += [this] (float val) mutable {
|
||||||
|
hue_label->SetContent(std::format("Hue: {}", Math::FloorInt(val*360)));
|
||||||
|
|
||||||
|
hue = val * 360;
|
||||||
|
|
||||||
|
hue_slider->ScrubberColor(Color4::FromHSV(hue, 1.f, 1.f));
|
||||||
|
|
||||||
|
OnColorValueChanged.Invoke(Color4::FromHSV(hue, sat, bri));
|
||||||
|
};
|
||||||
|
|
||||||
|
hue_label = new JUI::Text(hue_slider);
|
||||||
|
hue_label->AlignCenterHorizontally();
|
||||||
|
hue_label->AlignTop();
|
||||||
|
hue_label->SetTextColor(Colors::Black);
|
||||||
|
hue_label->SetContent("Hue: 0");
|
||||||
|
|
||||||
|
sat_slider = new JUI::Slider(row_layout);
|
||||||
|
sat_slider->Size({100_percent, 28_px});
|
||||||
|
sat_slider->Minimum(0); sat_slider->Maximum(1);
|
||||||
|
sat_slider->Interval(1e-3f);
|
||||||
|
sat_slider->ValueChanged += [this] (float val) mutable {
|
||||||
|
sat_label->SetContent(std::format("Saturation: {}%", val*100.f));
|
||||||
|
|
||||||
|
sat = val;
|
||||||
|
OnColorValueChanged.Invoke(Color4::FromHSV(hue, sat, bri));
|
||||||
|
};
|
||||||
|
|
||||||
|
sat_label = new JUI::Text(sat_slider);
|
||||||
|
sat_label->AlignCenterHorizontally();
|
||||||
|
sat_label->AlignTop();
|
||||||
|
sat_label->SetTextColor(Colors::Black);
|
||||||
|
sat_label->SetContent("Saturation: 100%");
|
||||||
|
|
||||||
|
bri_slider = new JUI::Slider(row_layout);
|
||||||
|
bri_slider->Size({100_percent, 28_px});
|
||||||
|
bri_slider->Minimum(0); bri_slider->Maximum(1);
|
||||||
|
bri_slider->Interval(1e-3f);
|
||||||
|
bri_slider->ValueChanged += [this] (float val) mutable {
|
||||||
|
bri_label->SetContent(std::format("Brightness: {}%", val*100.f));
|
||||||
|
|
||||||
|
bri = val;
|
||||||
|
|
||||||
|
OnColorValueChanged.Invoke(Color4::FromHSV(hue, sat, bri));
|
||||||
|
};
|
||||||
|
|
||||||
|
bri_label = new JUI::Text(bri_slider);
|
||||||
|
bri_label->AlignCenterHorizontally();
|
||||||
|
bri_label->AlignTop();
|
||||||
|
bri_label->SetTextColor(Colors::Black);
|
||||||
|
bri_label->SetContent("Brightness: 100%");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
auto* hue_box = new JUI::Rect();
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit ColorPicker(Widget* parent) : ColorPicker() {
|
||||||
|
Parent(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
JUI::Slider* hue_slider;
|
||||||
|
JUI::Slider* sat_slider;
|
||||||
|
JUI::Slider* bri_slider;
|
||||||
|
JUI::Text* hue_label;
|
||||||
|
JUI::Text* sat_label;
|
||||||
|
JUI::Text* bri_label;
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReShaderProgram : public ReWindow::OpenGLWindow {
|
||||||
|
public:
|
||||||
|
JUI::Scene *scene;
|
||||||
|
JUI::UtilityBar* toolbar;
|
||||||
|
JUI::CommandLine *console;
|
||||||
|
JUI::Window* info_dialog;
|
||||||
|
JGL::Shader shader;
|
||||||
|
JGL::RenderTarget *canvas;
|
||||||
|
float u_time;
|
||||||
|
float u_scale = 2.f;
|
||||||
|
Vector2 u_translation {0.4f, 0.f};
|
||||||
|
Color4 u_rgb_1 = Colors::Greens::Chartreuse;
|
||||||
|
Color4 u_rgb_2 = Colors::Reds::LightCoral;
|
||||||
|
std::string frag_name;
|
||||||
|
std::string vert_name;
|
||||||
|
|
||||||
|
ReShaderProgram();
|
||||||
|
|
||||||
|
static std::filesystem::path VertexShaderFilepathFromPrefixName(const std::string &name)
|
||||||
|
{
|
||||||
|
return "../shaders/" + name + ".vert.glsl";
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::filesystem::path FragmentShaderFilepathFromPrefixName(const std::string &name)
|
||||||
|
{
|
||||||
|
return "../shaders/" + name + ".frag.glsl";
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadShader(const std::string& name)
|
||||||
|
{
|
||||||
|
frag_name = name;
|
||||||
|
vert_name = name;
|
||||||
|
shader = Shader(VertexShaderFilepathFromPrefixName(name), FragmentShaderFilepathFromPrefixName(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadShader(const std::string& vertex_name, const std::string& fragment_name)
|
||||||
|
{
|
||||||
|
this->vert_name = vertex_name;
|
||||||
|
this->frag_name = fragment_name;
|
||||||
|
shader = Shader(VertexShaderFilepathFromPrefixName(vert_name), FragmentShaderFilepathFromPrefixName(frag_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadShaderWithDefaultVertex(const std::string& name) {
|
||||||
|
this->frag_name = name;
|
||||||
|
shader = Shader(VertexShaderFilepathFromPrefixName(vert_name), FragmentShaderFilepathFromPrefixName(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReloadShader() {
|
||||||
|
LoadShader(vert_name, frag_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
using CmdArgs = std::vector<std::string>;
|
||||||
|
void PositionCmd(const CmdArgs& args) {
|
||||||
|
if (args.empty()) { // Output position + scale
|
||||||
|
console->Log(std::format("Pos: {}, {}, Zoom: {}", u_translation.x, u_translation.y, u_scale));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.size() >= 2) { // Set Position
|
||||||
|
u_translation.x = std::stof(args[0]);
|
||||||
|
u_translation.y = std::stof(args[1]);
|
||||||
|
if (args.size() >= 3) { // Also set scale
|
||||||
|
u_scale = std::stof(args[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console->Log(std::format("{}", args.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateMenu() {
|
||||||
|
using namespace JUI::UDimLiterals;
|
||||||
|
|
||||||
|
toolbar = new JUI::UtilityBar(scene);
|
||||||
|
toolbar->AddSubmenu("File"); toolbar->AddSubmenu("Edit");
|
||||||
|
auto* btn_toggle_console = toolbar->AddButton("Console");
|
||||||
|
|
||||||
|
btn_toggle_console->OnClickEvent += [this] (auto a, auto b) mutable {
|
||||||
|
console->Toggle();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto* btn_toggle_info_dialog = toolbar->AddButton("Info");
|
||||||
|
|
||||||
|
// TODO: Implement JUI structure that makes blocks of text easy to impelement.
|
||||||
|
info_dialog = new JUI::Window(scene);
|
||||||
|
info_dialog->SetTitle("FractalInspector - Read Me");
|
||||||
|
|
||||||
|
btn_toggle_info_dialog->OnClickEvent += [this] (auto a, auto b) mutable {
|
||||||
|
info_dialog->Toggle();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto* layout = new JUI::VerticalListLayout(info_dialog->ViewportInstance());
|
||||||
|
layout->Padding(0_px);
|
||||||
|
|
||||||
|
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, 25_px});
|
||||||
|
content->SetContent(text);
|
||||||
|
content->SetTextSize(size);
|
||||||
|
content->SetTextColor(color);
|
||||||
|
content->BGColor(Colors::Transparent);
|
||||||
|
content->AlignCenterHorizontally();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Is this text useful and relevant, or does it come off as egotistic autofellatio?
|
||||||
|
line_item("FractalInspector V 1.0");
|
||||||
|
line_item("Developed & Maintained by Josh O'Leary");
|
||||||
|
line_item("(c) 2025 Redacted Software - redacted.cc");
|
||||||
|
line_item("This project is part of an independent research project to develop high-performance,", 18);
|
||||||
|
line_item("useful software that stands in contrast to all modern software trends.", 18);
|
||||||
|
line_item("This program is written in C++, from scratch.");
|
||||||
|
|
||||||
|
// TODO: Utilize for things later.
|
||||||
|
auto* wind = new JUI::Window(scene);
|
||||||
|
wind->SetTitle("Color-Picker A");
|
||||||
|
wind->MinSize({200, 150});
|
||||||
|
wind->Size({200_px, 150_px});
|
||||||
|
wind->Position({50_px, 50_px});
|
||||||
|
//wind->Close();
|
||||||
|
|
||||||
|
ColorPicker* pp = new ColorPicker(wind->ViewportInstance());
|
||||||
|
pp->OnColorValueChanged += [this] (const Color4& color) mutable {
|
||||||
|
u_rgb_1 = color;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
auto* wind2 = new JUI::Window(scene);
|
||||||
|
wind2->SetTitle("Color-Picker B");
|
||||||
|
wind2->MinSize({200, 150});
|
||||||
|
wind2->Size({200_px, 150_px});
|
||||||
|
wind2->Position({50_px, 250_px});
|
||||||
|
|
||||||
|
ColorPicker* pp2 = new ColorPicker(wind2->ViewportInstance());
|
||||||
|
pp2->OnColorValueChanged += [this] (const Color4& color) mutable {
|
||||||
|
u_rgb_2 = color;
|
||||||
|
};
|
||||||
|
|
||||||
|
console = new JUI::CommandLine(scene);
|
||||||
|
console->MinSize(Vector2(200, 200));
|
||||||
|
console->MaxSize(Vector2(GetWidth()-10, GetHeight()-10));
|
||||||
|
console->Size({100_percent, 200_px});
|
||||||
|
console->Position({0_percent, JUI::UDim(-200, 1)});
|
||||||
|
console->OnInput += [this] (const std::string& message) {
|
||||||
|
auto tokens = string_expand(message);
|
||||||
|
|
||||||
|
if (tokens.size() == 0) {
|
||||||
|
console->Log("No command input!", Colors::Red);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cmd = tokens[0];
|
||||||
|
|
||||||
|
// Remove 0th element from tokens before passing to command delegates.
|
||||||
|
tokens.erase(tokens.begin());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (cmd == "r" || cmd == "reload") {
|
||||||
|
ReloadShader();
|
||||||
|
} else if (cmd == "lf") {
|
||||||
|
if (tokens.size() > 1) {
|
||||||
|
std::string fragment = tokens[1];
|
||||||
|
// TODO: Check for file in question.
|
||||||
|
LoadShaderWithDefaultVertex(fragment);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console->Log("Error: Command syntax is lf <fragment-shader-name>", Colors::Red);
|
||||||
|
} else if (cmd == "ls") {
|
||||||
|
if (tokens.size() > 1) {
|
||||||
|
std::string shader = tokens[1];
|
||||||
|
// TODO: Check for file in question.
|
||||||
|
LoadShader(shader);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console->Log("Error: Command syntax is ls <shader-name>", Colors::Red);
|
||||||
|
} else if (cmd == "pos") {
|
||||||
|
return PositionCmd(tokens);
|
||||||
|
} else {
|
||||||
|
console->Log(std::format("No such command: {}", cmd), Colors::Red);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Open() override
|
||||||
|
{
|
||||||
|
if (!OpenGLWindow::Open())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto size = GetSize();
|
||||||
|
auto vec_size = Vector2i(size.x, size.y);
|
||||||
|
if (!JGL::Init(vec_size, 0.f, 1.f))
|
||||||
|
return false;
|
||||||
|
JGL::Update(vec_size);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||||
|
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glDepthFunc(GL_LESS);
|
||||||
|
glDepthMask(GL_TRUE);
|
||||||
|
// TODO: Delete when we update to the next release of JGL
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // NOTE: This MUST be called for text rendering to work properly!!!
|
||||||
|
|
||||||
|
scene = new JUI::Scene();
|
||||||
|
CreateMenu();
|
||||||
|
|
||||||
|
canvas = new RenderTarget(vec_size);
|
||||||
|
LoadShader("test", "mandelbrot");
|
||||||
|
|
||||||
|
console->Log(std::format("OpenGL Renderer: {}", GetGLRenderer()));
|
||||||
|
console->Log(std::format("OpenGL Vendor: {}", GetGLVendor()));
|
||||||
|
console->Log(std::format("OpenGL Version: {}", GetGLVersion()));
|
||||||
|
console->Log(std::format("GLSL Version: {}", GetGLSLVersion()));
|
||||||
|
/*auto ext_list = GetGLExtensionList();
|
||||||
|
|
||||||
|
// TODO: output 3 at a time.
|
||||||
|
console->Log("GL-Extensions: ");
|
||||||
|
std::string formatted_output = "";//"GL-Extensions: {}";
|
||||||
|
|
||||||
|
|
||||||
|
int iter = 0;
|
||||||
|
for (auto& str : ext_list) {
|
||||||
|
formatted_output += str + ", ";
|
||||||
|
iter++;
|
||||||
|
if (iter > 5) {
|
||||||
|
iter = 0;
|
||||||
|
console->Log(formatted_output);
|
||||||
|
formatted_output = "";
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void PropagateWindowSize() {
|
||||||
|
auto size = GetSize();
|
||||||
|
Vector2i vSize = Vector2i(size.x, size.y);
|
||||||
|
JGL::Update(vSize);
|
||||||
|
scene->SetViewportSize(Vector2(vSize));
|
||||||
|
console->MaxSize(Vector2(GetWidth()-10, GetHeight()-10));
|
||||||
|
|
||||||
|
// TODO: Causes the shader canvas to not appear...?
|
||||||
|
//canvas->Resize(vSize);
|
||||||
|
}
|
||||||
|
void Update(float elapsed) {
|
||||||
|
scene->Update(elapsed);
|
||||||
|
|
||||||
|
if (IsKeyDown(Keys::F6)) {
|
||||||
|
u_scale = 2.f;
|
||||||
|
u_translation = {.4, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Vary the rate appropriately.
|
||||||
|
float rate = elapsed * 0.125f;
|
||||||
|
|
||||||
|
if (IsKeyDown(Keys::LeftShift))
|
||||||
|
rate *= 0.125f;
|
||||||
|
|
||||||
|
if (IsKeyDown(Keys::LeftControl))
|
||||||
|
rate *= 0.125f;
|
||||||
|
|
||||||
|
if (IsKeyDown(Keys::Minus))
|
||||||
|
u_scale += rate;
|
||||||
|
if (IsKeyDown(Keys::Equals))
|
||||||
|
u_scale -= rate;
|
||||||
|
|
||||||
|
if (IsKeyDown(Keys::DownArrow))
|
||||||
|
u_translation.y += rate;
|
||||||
|
if (IsKeyDown(Keys::UpArrow))
|
||||||
|
u_translation.y -= rate;
|
||||||
|
|
||||||
|
if (IsKeyDown(Keys::LeftArrow))
|
||||||
|
u_translation.x += rate;
|
||||||
|
if (IsKeyDown(Keys::RightArrow))
|
||||||
|
u_translation.x -= rate;
|
||||||
|
|
||||||
|
|
||||||
|
u_time += elapsed;
|
||||||
|
|
||||||
|
Vector2 u_res = Vector2(GetSize().x, GetSize().y);
|
||||||
|
|
||||||
|
// TODO: Why do we need to multiply X by 1.5 here?
|
||||||
|
Vector2 u_mouse = Vector2(GetMouseCoordinates().x*1.5f, GetSize().y-GetMouseCoordinates().y);
|
||||||
|
|
||||||
|
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.SetFloat("u_scale", u_scale);
|
||||||
|
shader.SetVector2("u_translation", u_translation);
|
||||||
|
}
|
||||||
|
void Draw() {
|
||||||
|
|
||||||
|
Shader::UseDefault();
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glLoadIdentity();
|
||||||
|
|
||||||
|
// TODO: Have to be particular about the order-of-operations in regards to shaders.
|
||||||
|
// Protip: Use RenderTarget to draw a quad with the shader in question. *After rendering the rest of the scene.*
|
||||||
|
|
||||||
|
J2D::Begin();
|
||||||
|
J2D::DrawRenderTarget(canvas, {0, 0});
|
||||||
|
J2D::End();
|
||||||
|
|
||||||
|
scene->Draw();
|
||||||
|
|
||||||
|
JGL::J2D::Begin(canvas, true);
|
||||||
|
shader.Use();
|
||||||
|
JGL::J2D::FillRect(Colors::Black, {0, 0}, Vector2(GetSize().x, GetSize().y));
|
||||||
|
JGL::J2D::End();
|
||||||
|
|
||||||
|
}
|
||||||
|
void OnRefresh(float elapsed) override {
|
||||||
|
PropagateWindowSize();
|
||||||
|
Update(elapsed);
|
||||||
|
Draw();
|
||||||
|
this->SwapBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnMouseButtonDown(const ReWindow::MouseButtonDownEvent &e) override {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
void OnMouseButtonUp(const ReWindow::MouseButtonUpEvent &e) override {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
void OnMouseMove(const ReWindow::MouseMoveEvent &e) override {
|
||||||
|
Vector2 nmp = Vector2(e.Position.x, e.Position.y);
|
||||||
|
scene->ObserveMouseMovement(nmp);
|
||||||
|
}
|
||||||
|
void OnKeyDown(const ReWindow::KeyDownEvent &e) override {
|
||||||
|
if (scene->ObserveKeyInput(e.key, true))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (e.key == Keys::R)
|
||||||
|
ReloadShader();
|
||||||
|
}
|
||||||
|
void OnKeyUp(const ReWindow::KeyUpEvent &e) override {
|
||||||
|
scene->ObserveKeyInput(e.key, false);
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
ReShaderProgram::ReShaderProgram(): ReWindow::OpenGLWindow("ReShader", 1800, 1000, 2, 1) {
|
||||||
|
Shader::OnCompilationErrorMessage += [this](std::string type, std::string infoLog) {
|
||||||
|
auto log_lines = 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
u_time = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
ReWindow::Logger::Debug.EnableConsole(false);
|
||||||
|
|
||||||
|
auto* program = new ReShaderProgram();
|
||||||
|
|
||||||
|
program->Open();
|
||||||
|
program->SetFullscreen(false);
|
||||||
|
program->SetVsyncEnabled(false);
|
||||||
|
program->SetResizable(true);
|
||||||
|
|
||||||
|
while (program->IsAlive())
|
||||||
|
program->ManagedRefresh();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
106
shaders/mandelbrot.frag.glsl
Normal file
106
shaders/mandelbrot.frag.glsl
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#version 120
|
||||||
|
|
||||||
|
#ifdef GL_ES
|
||||||
|
precision highp float;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PI 3.14159265359
|
||||||
|
|
||||||
|
uniform vec2 u_resolution;
|
||||||
|
uniform vec2 u_mouse;
|
||||||
|
uniform float u_time;
|
||||||
|
|
||||||
|
uniform vec2 u_translation;
|
||||||
|
uniform float u_scale;
|
||||||
|
|
||||||
|
uniform vec3 u_rgb_1;
|
||||||
|
uniform vec3 u_rgb_2;
|
||||||
|
|
||||||
|
#define N 256. // Number of iterations?
|
||||||
|
#define B 4. // What does B mean?
|
||||||
|
|
||||||
|
|
||||||
|
vec3 pal(in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) {
|
||||||
|
return a + b*cos(6.28318*(c*t+d));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The mandelbrot set is a set of complex numbers c for which the function:
|
||||||
|
// f(z) = z*z + c
|
||||||
|
// stays bounded between a certain range of values when iterated from z = 0.
|
||||||
|
|
||||||
|
#define MAX_ITERATIONS 100.
|
||||||
|
|
||||||
|
vec2 square_imaginary(vec2 number) {
|
||||||
|
return vec2(
|
||||||
|
pow(number.x, 2) - pow(number.y, 2),
|
||||||
|
2*number.x*number.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
float iterate_mandelbrot(vec2 coord) {
|
||||||
|
vec2 z = vec2(0,0);
|
||||||
|
for (int i = 0; i < MAX_ITERATIONS; i++) {
|
||||||
|
z = square_imaginary(z) + coord;
|
||||||
|
if (length(z) > 2) return i / MAX_ITERATIONS;
|
||||||
|
}
|
||||||
|
return MAX_ITERATIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
float iterate(vec2 p) {
|
||||||
|
vec2 z = vec2(0), c = p;
|
||||||
|
float i;
|
||||||
|
for (i=0.; i < N; i++ ) {
|
||||||
|
z = mat2(z, -z.y, z.x) * z + c;
|
||||||
|
if (dot(z, z) > B*B) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//return i;
|
||||||
|
// Compute the iteration smoothly, instead of in integers.
|
||||||
|
|
||||||
|
if (p.y < 0.f) return i;
|
||||||
|
return i - log(log(dot(z, z)) / log(B)) / log(2.);;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 R = u_resolution.xy;
|
||||||
|
|
||||||
|
//float translate_h = .4;
|
||||||
|
//float translate_v = 0.0;
|
||||||
|
//float scale = 2.;
|
||||||
|
|
||||||
|
//vec2 translation = vec2(translate_h, translate_v);
|
||||||
|
|
||||||
|
vec2 uv = (u_scale * gl_FragCoord.xy - R - 1.) / R.y - u_translation;
|
||||||
|
//vec2 z = vec2(0), c = uv;
|
||||||
|
//float i;
|
||||||
|
|
||||||
|
//for (i = 0.; i < N; i++) {
|
||||||
|
// z = mat2(z, -z.y, z.x) * z + c;
|
||||||
|
// if (dot(z, z) > B*B) break;
|
||||||
|
//}
|
||||||
|
|
||||||
|
float n = iterate(uv) / N;
|
||||||
|
|
||||||
|
vec3 col = pal(fract(n + 0.5), vec3(.5), u_rgb_1, u_rgb_2, vec3(.0, .1, .2));
|
||||||
|
|
||||||
|
//if (n == 1.) {n = 0; }
|
||||||
|
gl_FragColor = vec4(n == 1. ? vec3(0) : col, 1.);
|
||||||
|
|
||||||
|
//vec2 st = gl_FragCoord.xy/u_resolution.xy;
|
||||||
|
//vec3 color = vec3(0.0);
|
||||||
|
|
||||||
|
//vec3 pct = vec3(st.x);
|
||||||
|
|
||||||
|
// pct.r = smoothstep(0.0,1.0, st.x);
|
||||||
|
// pct.g = sin(st.x*PI);
|
||||||
|
// pct.b = pow(st.x,0.5);
|
||||||
|
|
||||||
|
//color = mix(colorA, colorB, pct);
|
||||||
|
|
||||||
|
// Plot transition lines for each channel
|
||||||
|
// color = mix(color,vec3(1.0,0.0,0.0),plot(st,pct.r));
|
||||||
|
// color = mix(color,vec3(0.0,1.0,0.0),plot(st,pct.g));
|
||||||
|
// color = mix(color,vec3(0.0,0.0,1.0),plot(st,pct.b));
|
||||||
|
|
||||||
|
// gl_FragColor = vec4(color,1.0);
|
||||||
|
}
|
6
shaders/test.vert.glsl
Normal file
6
shaders/test.vert.glsl
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#version 120
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
//gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||||
|
gl_Position = ftransform();
|
||||||
|
}
|
Reference in New Issue
Block a user