Files
ReShader/main.cpp
2025-04-22 01:11:39 -05:00

298 lines
9.6 KiB
C++

#include <iostream>
#include <JGL/logger/logger.h>
#include <ReWindow/types/Window.h>
#include <JUI/Widgets/Scene.hpp>
#include <JUI/Widgets/Window.hpp>
#include "JUI/Widgets/CommandLine.hpp"
#include "JGL/types/Shader.h"
#include <ReWindow/Logger.h>
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;
}
class ReShaderProgram : public ReWindow::OpenGLWindow {
public:
JUI::Scene *scene;
JUI::CommandLine *console;
JGL::Shader shader;
JGL::RenderTarget *canvas;
float u_time;
std::string frag_name;
std::string vert_name;
ReShaderProgram() : ReWindow::OpenGLWindow("ReShader", 1080, 720, 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;
}
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);
}
void CreateMenu() {
using namespace JUI::UDimLiterals;
// TODO: Utilize for things later.
auto* wind = new JUI::Window(scene);
wind->SetTitle("ReShader");
wind->MinSize({100, 100});
wind->Size({200_px, 100_px});
wind->Close();
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);
std::string cmd = tokens[0];
if (tokens.size() == 0)
{
console->Log("No command input!", Colors::Red);
return;
}
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 {
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");
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);
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);
}
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:
};
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;
}