git.haldean.org ana / 430c158 src / shaders.cpp
430c158

Tree @430c158 (Download .tar.gz)

shaders.cpp @430c158raw · history · blame

#include "shaders.hpp"
#include "glerr.hpp"
#include "node.hpp"

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <string>

namespace ana {
namespace shaders {

static const std::string node_vert_src = R"(
#version 330 core
layout (location = 0) in vec4 data;

out NODE_DATA {
    vec2 size;
} node_data;

uniform mat4 viewport;

void main(void) {
    gl_Position = viewport * vec4(data[0], data[1], 0.0, 1.0);
    vec4 s = viewport * vec4(data[2], data[3], 0.0, 0.0);
    node_data.size = vec2(s[0], s[1]);
}
)";

static const std::string node_frag_src = R"(
#version 330 core
out vec4 Color;

void main()
{
    Color = vec4(1.0, 1.0, 1.0, 0.8);
}
)";

static const std::string node_geo_src = R"(
#version 330 core
layout (points) in;
layout (triangle_strip, max_vertices = 4) out;
in NODE_DATA {
    vec2 size;
} nd[];

void main() {
    vec4 p = gl_in[0].gl_Position;
    vec2 s = nd[0].size;
    gl_Position = p;
    EmitVertex();
    gl_Position = p + vec4(s[0], 0, 0, 0);
    EmitVertex();
    gl_Position = p + vec4(0, s[1], 0, 0);
    EmitVertex();
    gl_Position = p + vec4(s[0], s[1], 0, 0);
    EmitVertex();
    EndPrimitive();
}
)";

static GLuint node_vao;
static GLuint node_program_id;

static GLint node_data_attr;
static GLint node_viewport_attr;

static GLuint node_data_buf;

void print_log(GLuint object) {
    GLint log_length = 0;
    if (glIsShader(object)) {
        glGetShaderiv(object, GL_INFO_LOG_LENGTH, &log_length);
    } else if (glIsProgram(object)) {
        glGetProgramiv(object, GL_INFO_LOG_LENGTH, &log_length);
    } else {
        std::cerr << "print_log called with invalid object" << std::endl;
        return;
    }

    char* log = (char*)malloc(log_length);
    if (glIsShader(object))
        glGetShaderInfoLog(object, log_length, NULL, log);
    else if (glIsProgram(object))
        glGetProgramInfoLog(object, log_length, NULL, log);
    std::cerr << log << std::endl;
    free(log);
}

GLuint create_shader_from_string(const std::string &src, GLenum type) {
    GLuint res = glCreateShader(type);
    const GLchar *csrc = src.c_str();
    GLint len = src.length();
    glShaderSource(res, 1, &csrc, &len);
    glCompileShader(res);

    GLint compile_ok = GL_FALSE;
    glGetShaderiv(res, GL_COMPILE_STATUS, &compile_ok);
    if (compile_ok == GL_FALSE) {
        std::cerr << "[E] error loading shader: " << std::endl;
        print_log(res);
        glDeleteShader(res);
        return 0;
    }
    return res;
}

bool init(void) {
    GLuint vs, fs, gs;
    if (!(vs = create_shader_from_string(node_vert_src, GL_VERTEX_SHADER))) {
        std::cerr << "[E] vert shader couldn't be loaded" << std::endl;
        return false;
    }
    if (!(fs = create_shader_from_string(node_frag_src, GL_FRAGMENT_SHADER))) {
        std::cerr << "[E] frag shader couldn't be loaded" << std::endl;
        return false;
    }
    if (!(gs = create_shader_from_string(node_geo_src, GL_GEOMETRY_SHADER))) {
        std::cerr << "[E] geometry shader couldn't be loaded" << std::endl;
        return false;
    }
    node_program_id = glCreateProgram();
    glAttachShader(node_program_id, vs);
    glAttachShader(node_program_id, fs);
    glAttachShader(node_program_id, gs);
    glLinkProgram(node_program_id);
    GLint link_ok;
    glGetProgramiv(node_program_id, GL_LINK_STATUS, &link_ok);
    if (!link_ok) {
        std::cerr << "[E] glLinkProgram: ";
        print_log(node_program_id);
        std::cerr << std::endl << "vert src:" << std::endl << node_vert_src;
        std::cerr << std::endl << "frag src:" << std::endl << node_frag_src;
        std::cerr << std::endl << "geo src:" << std::endl << node_geo_src;
        node_program_id = 0;
        return false;
    }

    node_data_attr = glGetAttribLocation(node_program_id, "data");
    if (node_data_attr == -1) {
        std::cerr << "[E] glGetAttribLocation failed for node data" << std::endl;
        return false;
    }

    node_viewport_attr = glGetUniformLocation(node_program_id, "viewport");
    if (node_viewport_attr == -1) {
        std::cerr << "[E] glGetAttribLocation failed for viewport matrix" << std::endl;
        return false;
    }

    glGenVertexArrays(1, &node_vao);
    INSERT_GL_ERROR_CHECK;
    glBindVertexArray(node_vao);
    INSERT_GL_ERROR_CHECK;
    glGenBuffers(1, &node_data_buf);
    INSERT_GL_ERROR_CHECK;
    glBindVertexArray(0);

    return true;
}

void draw(const ana::ui &ui, const ana::nodeset &nodes) {
    static GLfloat *node_data = nullptr;
    static size_t node_data_size = 0;
    std::cout << "new center: " << ui.center.x << ", " << ui.center.y << std::endl;

    size_t elems = nodes.size() * 4;
    if (elems != node_data_size) {
        node_data = (GLfloat*) reallocarray(node_data, elems, sizeof(GLfloat));
    }
    int i = 0;
    for (const ana::node &n : nodes.nodes) {
        node_data[4 * i + 0] = n.loc.x;
        node_data[4 * i + 1] = n.loc.y;
        node_data[4 * i + 2] = n.size.x;
        node_data[4 * i + 3] = n.size.y;
        i++;
    }

    glUseProgram(node_program_id); INSERT_GL_ERROR_CHECK;

    glBindVertexArray(node_vao); INSERT_GL_ERROR_CHECK;
    glBindBuffer(GL_ARRAY_BUFFER, node_data_buf); INSERT_GL_ERROR_CHECK;
    glBufferData(GL_ARRAY_BUFFER, elems * sizeof(GLfloat), node_data, GL_DYNAMIC_DRAW); INSERT_GL_ERROR_CHECK;

    glm::mat4 m = glm::ortho(
        ui.center.x - ui.view, ui.center.x + ui.view,
        ui.center.y - ui.view * ui.aspect, ui.center.y + ui.view * ui.aspect,
        -1.f, 1.f);
    glUniformMatrix4fv(node_viewport_attr, 1, GL_FALSE, glm::value_ptr(m));

    glEnableVertexAttribArray(node_data_attr); INSERT_GL_ERROR_CHECK;
    glBindBuffer(GL_ARRAY_BUFFER, node_data_buf); INSERT_GL_ERROR_CHECK;
    glVertexAttribPointer(node_data_attr, 4, GL_FLOAT, GL_FALSE, 0, 0); INSERT_GL_ERROR_CHECK;

    glDrawArrays(GL_POINTS, 0, nodes.size()); INSERT_GL_ERROR_CHECK;
    glDisableVertexAttribArray(node_data_attr); INSERT_GL_ERROR_CHECK;
    glBindVertexArray(0);
}

void destroy(void) {
    glDeleteProgram(node_program_id);
    glDeleteBuffers(1, &node_data_buf);
}

}
}