672 lines
19 KiB
C++
672 lines
19 KiB
C++
#include <GLFW/glfw3.h>
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <curl/curl.h>
|
|
|
|
#include "json.hpp"
|
|
#include "imgui.h"
|
|
#include "imgui_impl_glfw_gl3.h"
|
|
#include "glad/glad.h"
|
|
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#include "stb_image.h"
|
|
|
|
static void error_callback(int, const char* description)
|
|
{
|
|
fprintf(stderr, "Error: %s\n", description);
|
|
}
|
|
|
|
GLFWwindow* window;
|
|
|
|
std::map<std::string, GLuint> external_resources;
|
|
|
|
struct MemoryStruct {
|
|
char *memory;
|
|
size_t size;
|
|
};
|
|
|
|
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
|
{
|
|
size_t realsize = size * nmemb;
|
|
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
|
|
|
|
mem->memory = reinterpret_cast<char*>(realloc(mem->memory, mem->size + realsize + 1));
|
|
if(mem->memory == NULL) {
|
|
printf("not enough memory!\n");
|
|
return 0;
|
|
}
|
|
|
|
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
|
mem->size += realsize;
|
|
mem->memory[mem->size] = 0;
|
|
|
|
return realsize;
|
|
}
|
|
|
|
void grab_external(const std::string& resource)
|
|
{
|
|
CURL *curl_handle;
|
|
CURLcode res;
|
|
|
|
struct MemoryStruct chunk;
|
|
|
|
chunk.memory = reinterpret_cast<char*>(malloc(1));
|
|
chunk.size = 0;
|
|
|
|
curl_handle = curl_easy_init();
|
|
|
|
std::string url = "http://www.shadertoy.com" + resource;
|
|
|
|
std::cout << "requesting external resource " << url << std::endl;
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
|
|
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
|
|
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
|
|
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
|
|
|
|
res = curl_easy_perform(curl_handle);
|
|
|
|
if(res != CURLE_OK) {
|
|
fprintf(stderr, "curl_easy_perform() failed: %s\n",
|
|
curl_easy_strerror(res));
|
|
}
|
|
else
|
|
{
|
|
printf("%lu bytes retrieved\n", (long)chunk.size);
|
|
}
|
|
|
|
curl_easy_cleanup(curl_handle);
|
|
|
|
int width, height, channels;
|
|
stbi_uc* pixels = stbi_load_from_memory(reinterpret_cast<const stbi_uc*>(chunk.memory), chunk.size, &width, &height, &channels, STBI_rgb_alpha);
|
|
|
|
GLuint tex;
|
|
glGenTextures(1, &tex);
|
|
glBindTexture(GL_TEXTURE_2D, tex);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_FLOAT, pixels);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
external_resources.emplace(resource, tex);
|
|
|
|
free(chunk.memory);
|
|
}
|
|
|
|
const char* vertex_source = "in vec3 inPosition;\n"
|
|
"\n"
|
|
"out vec2 fragCoord;\n"
|
|
"\n"
|
|
"void main() {\n"
|
|
" gl_Position = vec4(inPosition, 1.0);\n"
|
|
" fragCoord = inPosition.xy;\n"
|
|
"}";
|
|
|
|
GLuint m_quadVAO, m_quadVBO;
|
|
|
|
struct input
|
|
{
|
|
int channel = 0;
|
|
int id = 0;
|
|
std::string type, src;
|
|
};
|
|
|
|
struct output
|
|
{
|
|
int channel = 0;
|
|
int id = 0;
|
|
};
|
|
|
|
struct renderpass
|
|
{
|
|
std::vector<input> inputs;
|
|
std::vector<output> outputs;
|
|
|
|
std::string name;
|
|
std::string type;
|
|
|
|
char* shader_source = new char[100000];
|
|
|
|
GLuint program = 0;
|
|
GLuint framebuffer, color;
|
|
};
|
|
|
|
std::map<int, GLuint> resources;
|
|
std::vector<renderpass> renderpasses;
|
|
|
|
void RetrackResources()
|
|
{
|
|
resources.clear();
|
|
|
|
for(auto pass : renderpasses)
|
|
{
|
|
for(auto out : pass.outputs)
|
|
{
|
|
resources.emplace(out.id, pass.color);
|
|
}
|
|
|
|
for(auto in : pass.inputs)
|
|
{
|
|
if(in.type == "texture")
|
|
{
|
|
resources.emplace(in.id, external_resources.at(in.src));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void draw_quad()
|
|
{
|
|
if(m_quadVAO == 0)
|
|
{
|
|
GLfloat quadVertices[] = {
|
|
-1.0f, 1.0f, 0.0f, 1.0f,
|
|
-1.0f, -1.0f, 0.0f, 0.0f,
|
|
1.0f, -1.0f, 1.0f, 0.0f,
|
|
|
|
-1.0f, 1.0f, 0.0f, 1.0f,
|
|
1.0f, -1.0f, 1.0f, 0.0f,
|
|
1.0f, 1.0f, 1.0f, 1.0f
|
|
};
|
|
|
|
glGenVertexArrays(1, &m_quadVAO);
|
|
glBindVertexArray(m_quadVAO);
|
|
|
|
glGenBuffers(1, &m_quadVBO);
|
|
glBindBuffer(GL_ARRAY_BUFFER, m_quadVBO);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
|
|
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
|
|
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), reinterpret_cast<void*>(2 * sizeof(GLfloat)));
|
|
}
|
|
|
|
glBindVertexArray(m_quadVAO);
|
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
glBindVertexArray(0);
|
|
}
|
|
|
|
void createShader(renderpass& pass, const std::string& vertex, const std::string& fragment)
|
|
{
|
|
pass.program = glCreateProgram();
|
|
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
|
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
|
|
std::vector<GLchar const*> shaders = { vertex.c_str(), fragment.c_str() };
|
|
|
|
glShaderSource(vertexShader, 1, &shaders[0], NULL);
|
|
glShaderSource(fragmentShader, 1, &shaders[1], NULL);
|
|
|
|
glCompileShader(vertexShader);
|
|
|
|
GLint logLength;
|
|
glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &logLength);
|
|
|
|
if(logLength > 0)
|
|
{
|
|
std::vector<char> ShaderErrorMessage(static_cast<unsigned int>(logLength) + 1);
|
|
glGetShaderInfoLog(vertexShader, logLength, nullptr, &ShaderErrorMessage[0]);
|
|
|
|
if(ShaderErrorMessage.size() > 1)
|
|
printf("failed to compile vertex shader: %s", &ShaderErrorMessage[0]);
|
|
}
|
|
|
|
glCompileShader(fragmentShader);
|
|
|
|
glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &logLength);
|
|
|
|
if(logLength > 0)
|
|
{
|
|
std::vector<char> ShaderErrorMessage(static_cast<unsigned int>(logLength) + 1);
|
|
glGetShaderInfoLog(fragmentShader, logLength, nullptr, &ShaderErrorMessage[0]);
|
|
|
|
if(ShaderErrorMessage.size() > 1)
|
|
printf("failed to compile fragment shader: %s", &ShaderErrorMessage[0]);
|
|
}
|
|
|
|
glAttachShader(pass.program, vertexShader);
|
|
glAttachShader(pass.program, fragmentShader);
|
|
|
|
glLinkProgram(pass.program);
|
|
|
|
glGetProgramiv(pass.program, GL_INFO_LOG_LENGTH, &logLength);
|
|
|
|
if(logLength > 0)
|
|
{
|
|
std::vector<char> ProgramErrorMessage(static_cast<unsigned int>(logLength) + 1);
|
|
glGetProgramInfoLog(pass.program, logLength, nullptr, &ProgramErrorMessage[0]);
|
|
|
|
if(ProgramErrorMessage.size() > 1)
|
|
{
|
|
printf("failed to link program: %s", &ProgramErrorMessage[0]);
|
|
pass.program = 0;
|
|
}
|
|
}
|
|
|
|
glDeleteShader(vertexShader);
|
|
glDeleteShader(fragmentShader);
|
|
}
|
|
|
|
void createBuffer(renderpass& pass)
|
|
{
|
|
std::string shader_prefix = "#version 330\n#extension GL_ARB_separate_shader_objects : enable\n";
|
|
std::string fragment_prefix = "out vec4 outColor;\nuniform float iGlobalTime = 0.2;\nuniform vec3 iResolution = vec3(640.0, 480.0, 1.0);\nuniform float iTimeDelta = 1.0 / 6.0f;\nuniform vec4 iMouse;\n";
|
|
std::string fragment_suffix = "void main() { vec4 color = vec4(0.0,0.0,0.0,1.0); mainImage(color, gl_FragCoord.xy); outColor = color; }\n";
|
|
fragment_prefix += "uniform sampler2D iChannel0;\nuniform sampler2D iChannel1;\nuniform sampler2D iChannel2;\nuniform sampler2D iChannel3;\nuniform int iFrame;\n";
|
|
|
|
std::string vertex = shader_prefix + vertex_source;
|
|
std::string fragment = shader_prefix + fragment_prefix + pass.shader_source + fragment_suffix;
|
|
|
|
createShader(pass, vertex, fragment);
|
|
|
|
glGenFramebuffers(1, &pass.framebuffer);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, pass.framebuffer);
|
|
|
|
glGenTextures(1, &pass.color);
|
|
glBindTexture(GL_TEXTURE_2D, pass.color);
|
|
|
|
int display_w, display_h;
|
|
glfwGetFramebufferSize(window, &display_w, &display_h);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, display_w, display_h, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
glFramebufferTexture2D(
|
|
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pass.color, 0
|
|
);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
void recompile_shaders() {
|
|
for(auto& pass : renderpasses)
|
|
{
|
|
createBuffer(pass);
|
|
}
|
|
}
|
|
|
|
int edit_callback(ImGuiTextEditCallbackData* data)
|
|
{
|
|
recompile_shaders();
|
|
}
|
|
|
|
struct curl_fetch_st {
|
|
char *payload;
|
|
size_t size;
|
|
};
|
|
|
|
size_t curl_callback (void *contents, size_t size, size_t nmemb, void *userp) {
|
|
size_t realsize = size * nmemb;
|
|
struct curl_fetch_st *p = (struct curl_fetch_st *) userp;
|
|
|
|
p->payload = (char *) realloc(p->payload, p->size + realsize + 1);
|
|
|
|
if (p->payload == NULL) {
|
|
fprintf(stderr, "ERROR: Failed to expand buffer in curl_callback");
|
|
free(p->payload);
|
|
|
|
return -1;
|
|
}
|
|
|
|
memcpy(&(p->payload[p->size]), contents, realsize);
|
|
|
|
p->size += realsize;
|
|
p->payload[p->size] = 0;
|
|
|
|
return realsize;
|
|
}
|
|
|
|
CURLcode curl_fetch_url(CURL *ch, const char *url, struct curl_fetch_st *fetch) {
|
|
CURLcode rcode;
|
|
|
|
fetch->payload = (char *) calloc(1, sizeof(fetch->payload));
|
|
|
|
if (fetch->payload == NULL) {
|
|
fprintf(stderr, "ERROR: Failed to allocate payload");
|
|
return CURLE_FAILED_INIT;
|
|
}
|
|
|
|
fetch->size = 0;
|
|
|
|
curl_easy_setopt(ch, CURLOPT_URL, url);
|
|
curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, curl_callback);
|
|
curl_easy_setopt(ch, CURLOPT_WRITEDATA, (void *) fetch);
|
|
curl_easy_setopt(ch, CURLOPT_USERAGENT, "libcurl-agent/1.0");
|
|
curl_easy_setopt(ch, CURLOPT_TIMEOUT, 5);
|
|
curl_easy_setopt(ch, CURLOPT_FOLLOWLOCATION, 1);
|
|
curl_easy_setopt(ch, CURLOPT_MAXREDIRS, 1);
|
|
|
|
rcode = curl_easy_perform(ch);
|
|
|
|
return rcode;
|
|
}
|
|
|
|
int GetChannel(renderpass pass, int c)
|
|
{
|
|
RetrackResources();
|
|
|
|
for(auto in : pass.inputs)
|
|
{
|
|
if(in.channel == c)
|
|
{
|
|
if(resources.count(pass.inputs[c].id))
|
|
return resources.at(pass.inputs[c].id);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int GetFinalOutput()
|
|
{
|
|
for(auto pass : renderpasses)
|
|
{
|
|
if(pass.type == "image")
|
|
return pass.color;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void window_size_callback(GLFWwindow* window, int width, int height)
|
|
{
|
|
recompile_shaders();
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
glfwSetErrorCallback(error_callback);
|
|
if (!glfwInit())
|
|
return -1;
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
window = glfwCreateWindow(640, 480, "shaderboy", NULL, NULL);
|
|
if (!window)
|
|
glfwTerminate();
|
|
|
|
glfwMakeContextCurrent(window);
|
|
|
|
glfwSwapInterval(0);
|
|
|
|
gladLoadGL();
|
|
ImGui_ImplGlfwGL3_Init(window, true);
|
|
glfwSetWindowSizeCallback(window, window_size_callback);
|
|
|
|
char* example_code =
|
|
"void mainImage( out vec4 fragColor, in vec2 fragCoord )\n"
|
|
"{\n"
|
|
"\tvec2 uv = fragCoord.xy / iResolution.xy;\n"
|
|
"\tfragColor = vec4(uv,0.5+0.5*sin(iGlobalTime),1.0);\n"
|
|
"}";
|
|
|
|
renderpass defaultpass;
|
|
defaultpass.type = "image";
|
|
defaultpass.name = "Image";
|
|
defaultpass.shader_source = example_code;
|
|
|
|
output defaultout;
|
|
defaultout.channel = 0;
|
|
defaultout.id = 37;
|
|
|
|
defaultpass.outputs.push_back(defaultout);
|
|
renderpasses.push_back(defaultpass);
|
|
|
|
recompile_shaders();
|
|
|
|
char* shader_url;
|
|
shader_url = new char[512];
|
|
|
|
memset(shader_url, 0, 512);
|
|
|
|
recompile_shaders();
|
|
|
|
curl_global_init(CURL_GLOBAL_ALL);
|
|
|
|
int frames = 0;
|
|
|
|
bool paused = false;
|
|
while (!glfwWindowShouldClose(window))
|
|
{
|
|
ImGui_ImplGlfwGL3_NewFrame();
|
|
|
|
ImGui::Begin("shaderboy");
|
|
|
|
ImGui::Text("FPS: %1.f", ImGui::GetIO().Framerate);
|
|
|
|
if(ImGui::Button("Pause/Resume"))
|
|
paused = !paused;
|
|
|
|
ImGui::InputText("Shader URL", shader_url, 512);
|
|
|
|
if(ImGui::Button("Download"))
|
|
{
|
|
CURL *ch;
|
|
CURLcode rcode;
|
|
|
|
curl_fetch_st curl_fetch;
|
|
curl_fetch_st *cf = &curl_fetch;
|
|
curl_slist *headers = nullptr;
|
|
|
|
const char *url = "https://www.shadertoy.com/shadertoy";
|
|
|
|
if ((ch = curl_easy_init()) == nullptr) {
|
|
fprintf(stderr, "ERROR: Failed to create curl handle!");
|
|
return 1;
|
|
}
|
|
|
|
headers = curl_slist_append(headers, "Accept: application/json");
|
|
headers = curl_slist_append(headers, "Referer: https://www.shadertoy.com/");
|
|
headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
|
|
|
|
std::string u = std::string(shader_url);
|
|
std::string shaderID = u.substr(u.find_last_of("/") + 1, u.length());
|
|
std::cout << shaderID << std::endl;
|
|
|
|
curl_easy_setopt(ch, CURLOPT_CUSTOMREQUEST, "POST");
|
|
curl_easy_setopt(ch, CURLOPT_HTTPHEADER, headers);
|
|
std::string tmp = "s=%7B%20%22shaders%22%20%3A%20%5B%22" + shaderID + "%22%5D%20%7D";
|
|
|
|
curl_easy_setopt(ch, CURLOPT_POSTFIELDS, tmp.c_str());
|
|
|
|
rcode = curl_fetch_url(ch, url, cf);
|
|
|
|
curl_easy_cleanup(ch);
|
|
curl_slist_free_all(headers);
|
|
|
|
if (rcode != CURLE_OK || cf->size < 1) {
|
|
fprintf(stderr, "ERROR: Failed to fetch url (%s) - curl said: %s", url, curl_easy_strerror(rcode));
|
|
return 2;
|
|
}
|
|
|
|
if (cf->payload != NULL) {
|
|
try
|
|
{
|
|
std::string test = cf->payload;
|
|
test = test.substr(1, test.length() - 2);
|
|
|
|
nlohmann::json json = nlohmann::json::parse(test);
|
|
|
|
renderpasses.clear();
|
|
for(auto pass : json["renderpass"])
|
|
{
|
|
renderpass renderpass;
|
|
renderpass.name = pass["name"];
|
|
renderpass.type = pass["type"];
|
|
|
|
std::string code = pass["code"];
|
|
strcpy(renderpass.shader_source, code.c_str());
|
|
|
|
for(auto in : pass["inputs"])
|
|
{
|
|
input input;
|
|
input.channel = in["channel"];
|
|
input.id = in["id"];
|
|
input.type = in["ctype"];
|
|
input.src = in["src"];
|
|
|
|
if(input.type == "texture")
|
|
{
|
|
if(!external_resources.count(input.src))
|
|
grab_external(input.src);
|
|
}
|
|
|
|
renderpass.inputs.push_back(input);
|
|
}
|
|
|
|
for(auto ou : pass["outputs"])
|
|
{
|
|
output output;
|
|
output.id = ou["id"];
|
|
output.channel = ou["channel"];
|
|
|
|
renderpass.outputs.push_back(output);
|
|
}
|
|
|
|
renderpasses.push_back(renderpass);
|
|
}
|
|
|
|
recompile_shaders();
|
|
|
|
free(cf->payload);
|
|
}
|
|
catch(std::exception)
|
|
{}
|
|
} else {
|
|
/* error */
|
|
fprintf(stderr, "ERROR: Failed to populate payload");
|
|
/* free payload */
|
|
free(cf->payload);
|
|
/* return */
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
//TODO: add back shader editing
|
|
//ImGui::InputTextMultiline("Shader", shader_source, 100000, ImVec2(-1, -1), ImGuiInputTextFlags_CallbackAlways, edit_callback);
|
|
|
|
for(auto pass : renderpasses)
|
|
{
|
|
ImGui::Separator();
|
|
|
|
ImGui::Text("Name: %s", pass.name.c_str());
|
|
ImGui::Text("Type: %s", pass.type.c_str());
|
|
|
|
ImGui::Text("Inputs:");
|
|
ImGui::Indent();
|
|
for(auto input : pass.inputs)
|
|
{
|
|
ImGui::Text("Id: %i", input.id);
|
|
ImGui::Text("Channel: %i", input.channel);
|
|
ImGui::Text("Type: %s", input.type.c_str());
|
|
ImGui::Text("Src: %s", input.src.c_str());
|
|
}
|
|
ImGui::Unindent();
|
|
|
|
ImGui::Spacing();
|
|
|
|
ImGui::Text("Outputs:");
|
|
ImGui::Indent();
|
|
for(auto output : pass.outputs)
|
|
{
|
|
ImGui::Text("Id: %i", output.id);
|
|
ImGui::Text("Channel: %i", output.channel);
|
|
}
|
|
ImGui::Unindent();
|
|
}
|
|
|
|
ImGui::Text("Resources:");
|
|
for(auto res : resources)
|
|
{
|
|
ImGui::Text("%i", res.first);
|
|
}
|
|
|
|
if(ImGui::Button("Recompile"))
|
|
recompile_shaders();
|
|
|
|
ImGui::End();
|
|
|
|
int display_w, display_h;
|
|
glfwGetFramebufferSize(window, &display_w, &display_h);
|
|
glViewport(0, 0, display_w, display_h);
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
//render out passes
|
|
if(!paused)
|
|
{
|
|
for(auto pass : renderpasses)
|
|
{
|
|
if(pass.program != 0)
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, pass.framebuffer);
|
|
|
|
glUseProgram(pass.program);
|
|
|
|
glUniform1i(glGetUniformLocation(pass.program, "iChannel0"), 0);
|
|
glUniform1i(glGetUniformLocation(pass.program, "iChannel1"), 1);
|
|
glUniform1i(glGetUniformLocation(pass.program, "iChannel2"), 2);
|
|
glUniform1i(glGetUniformLocation(pass.program, "iChannel3"), 3);
|
|
glUniform1i(glGetUniformLocation(pass.program, "iFrame"), 0);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, GetChannel(pass, 0));
|
|
|
|
glActiveTexture(GL_TEXTURE1);
|
|
glBindTexture(GL_TEXTURE_2D, GetChannel(pass, 1));
|
|
|
|
glActiveTexture(GL_TEXTURE2);
|
|
glBindTexture(GL_TEXTURE_2D, GetChannel(pass, 2));
|
|
|
|
glActiveTexture(GL_TEXTURE3);
|
|
glBindTexture(GL_TEXTURE_2D, GetChannel(pass, 3));
|
|
|
|
glUniform1f(glGetUniformLocation(pass.program, "iGlobalTime"), glfwGetTime());
|
|
glUniform3f(glGetUniformLocation(pass.program, "iResolution"), display_w, display_h, 1.0);
|
|
|
|
int state = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT);
|
|
if(state == GLFW_PRESS)
|
|
{
|
|
double xpos, ypos;
|
|
glfwGetCursorPos(window, &xpos, &ypos);
|
|
|
|
glUniform4f(glGetUniformLocation(pass.program, "iMouse"), xpos, ypos, 0.0, 0.0);
|
|
}
|
|
|
|
draw_quad();
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, GetFinalOutput());
|
|
|
|
draw_quad();
|
|
|
|
ImGui::Render();
|
|
|
|
glfwSwapBuffers(window);
|
|
glfwPollEvents();
|
|
|
|
frames++;
|
|
}
|
|
|
|
curl_global_cleanup();
|
|
|
|
glfwDestroyWindow(window);
|
|
glfwTerminate();
|
|
}
|