/// Includes
#include <ctime>
#include <sstream>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
/// Constants
constexpr auto width = 640;
constexpr auto height = 480;
constexpr auto title = "";
/// Helpers
//// Debug
void GLAPIENTRY debug_message_callback(
GLenum /* source */,
GLenum /* type */,
GLuint /* id */,
GLenum /* severity */,
GLsizei /* length */,
GLchar const * message,
void const * /* param */
)
{
std::cerr << message << std::endl;
};
//// Uniform
void uniform(GLint location, int value ) { glUniform1i (location, value); }
void uniform(GLint location, bool value ) { glUniform1i (location, value); }
void uniform(GLint location, float value ) { glUniform1f (location, value); }
void uniform(GLint location, float const (& value)[1]) { glUniform1fv(location, 1, value); }
void uniform(GLint location, float const (& value)[2]) { glUniform2fv(location, 1, value); }
void uniform(GLint location, float const (& value)[3]) { glUniform3fv(location, 1, value); }
void uniform(GLint location, float const (& value)[4]) { glUniform4fv(location, 1, value); }
template<typename T>
void uniform(char const * name, T const & value)
{
GLuint program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&program);
GLint location = glGetUniformLocation(program, name);
uniform(location, value);
}
//// Write
void write_tga(char const * base)
{
char header[] = {
0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
(char)(width >> 0U),
(char)(width >> 8U),
(char)(height >> 0U),
(char)(height >> 8U),
4*8, 0,
};
char data[4*width*height];
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, data);
auto t = std::time(nullptr);
auto pt = std::put_time(std::localtime(&t), "%Y-%m-%d_%H-%M-%S");
std::ofstream((std::ostringstream{} << base << "_" << pt << ".tga").str())
.write(header, sizeof(header))
.write(data, sizeof(data));
}
/// Main
int main(int /*argc*/, char * argv[])
{
//// Window/context
glfwInit();
auto window = glfwCreateWindow(width, height, title, nullptr, nullptr);
glfwMakeContextCurrent(window);
glewInit();
//// Callbacks
glfwSetWindowUserPointer(window, argv[0]);
glfwSetKeyCallback(window,
[](GLFWwindow* w, int key, int /*scancode*/, int action, int /*mods*/)
{
auto ptr = glfwGetWindowUserPointer(w);
if (action != GLFW_RELEASE) return;
if (key == GLFW_KEY_Q) glfwSetWindowShouldClose(w, GLFW_TRUE);
if (key == GLFW_KEY_W) write_tga((char *)ptr);
}
);
//// Debug
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(debug_message_callback, nullptr);
//// Shader
auto make_program = [](char const * vert_source, char const * frag_source)
{
auto program = glCreateProgram();
auto vert = glCreateShader(GL_VERTEX_SHADER);
auto frag = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vert, 1, &vert_source, nullptr);
glShaderSource(frag, 1, &frag_source, nullptr);
glCompileShader(vert);
glCompileShader(frag);
glAttachShader(program, vert);
glAttachShader(program, frag);
glLinkProgram(program);
return program;
};
auto vert_source = R"(
#version 140
out vec2 tex_coord;
void main()
{
tex_coord = vec2[](
vec2(0, 0),
vec2(2, 0),
vec2(0, 2)
)[gl_VertexID];
gl_Position = vec4(tex_coord*2-1, 0, 1);
}
)";
auto tex_coord_program = make_program(vert_source, R"(
#version 140
uniform int view;
uniform bool debug;
uniform float time;
in vec2 tex_coord;
void main()
{
gl_FragColor = vec4(fract(tex_coord - float(!debug)*time), 0, 1);
// Exercise the debugging colors.
// <https://www.desmos.com/calculator/uijwefecqs>
if (view >= 2)
{
float x = tex_coord.x;
x -= 0.5;
x *= 10;
x = clamp(x, -3, +3);
x /= (3 + x) * (3 - x);
x *= (3 + 1) * (3 - 1);
x *= 0.5;
x += 0.5;
gl_FragColor = vec4(x / (1 / float(tex_coord.y > 0.5)));
}
bvec3 debug_color = bvec3(
any(lessThan (gl_FragColor, vec4(0))),
any(greaterThan(gl_FragColor, vec4(1))),
any(isinf (gl_FragColor)) ||
any(isnan (gl_FragColor))
);
if (debug && any(debug_color))
gl_FragColor = vec4(debug_color, 1);
}
)");
//// Main loop
int view = 1;
bool debug = true;
float time = (float)glfwGetTime();
while (glfwPollEvents(), !glfwWindowShouldClose(window))
{
///// Time
float pt = time;
float dt = (time = (float)glfwGetTime()) - pt;
///// Input
for (int num = 0; num <= 9; ++num)
if (glfwGetKey(window, GLFW_KEY_0 + num)) view = num;
if (glfwGetKey(window, GLFW_KEY_R)) debug = false;
if (glfwGetKey(window, GLFW_KEY_E)) debug = true;
///// Draw
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(tex_coord_program);
uniform("view", view);
uniform("debug", debug);
uniform("time", time);
uniform("dt", dt);
glDrawArrays(GL_TRIANGLES, 0, 3);
///// Swap
glfwSwapBuffers(window);
}
//// Cleanup
glfwDestroyWindow(window);
glfwTerminate();
}