From 94de2291e005f1c646fd779e8d32b2820e22e071 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Fri, 31 Jul 2020 16:54:31 -0700 Subject: *: initial implementation of pig This is nothing to write home about, but it provides a little sandbox for developing shader-generated textures in the spirit of shadertoy or the demoscene tool bonzomatic. It's more oriented towards developing shaders for use with libstage in the small games I've been hacking on. --- src/shader-node.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 src/shader-node.c (limited to 'src/shader-node.c') diff --git a/src/shader-node.c b/src/shader-node.c new file mode 100644 index 0000000..aa4f0b7 --- /dev/null +++ b/src/shader-node.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2018-2019 - Vito Caputo - + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include + +#include "glad.h" +#include "m4f.h" +#include "macros.h" +#include "shader.h" +#include "shader-node.h" + +typedef struct shader_node_t { + shader_t *shader; + shader_node_uniforms_func_t *uniforms_func; + void *uniforms_ctxt; + const m4f_t *transform; +} shader_node_t; + +static unsigned vbo, tcbo; + +static const float vertices[] = { + +1.f, +1.f, 0.f, + +1.f, -1.f, 0.f, + -1.f, +1.f, 0.f, + +1.f, -1.f, 0.f, + -1.f, -1.f, 0.f, + -1.f, +1.f, 0.f, +}; + + +/* TODO: verify that this is OK, I recall tutorials stating these are always + * in the range 0-1, but it seems perfectly OK to use -1..+1 which is more + * convenient here where these shader-textured quads appreciate being fed + * unit square coordinates with 0,0 @ the center. + */ +static const float texcoords[] = { + 1.f, 1.f, + 1.f, -1.f, + -1.f, 1.f, + 1.f, -1.f, + -1.f, -1.f, + -1.f, 1.f, +}; + + +/* TODO: the vertex shader should probably just be private to this listing, and maybe + * shader_pair_new() should be refactored to lookup uniforms and attributes as a + * separate step... + */ +static const char * shader_node_attributes[] = { + "i_vertex", + "i_texcoord" +}; + +static void shader_node_render(const stage_t *stage, void *object, float alpha, void *render_ctxt) +{ + shader_node_t *shader_node = object; + unsigned n_uniforms, n_attributes; + int *uniforms, *attributes; + + assert(stage); + assert(shader_node); + + shader_use(shader_node->shader, &n_uniforms, &uniforms, &n_attributes, &attributes); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glVertexAttribPointer(attributes[0], 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + glBindBuffer(GL_ARRAY_BUFFER, tcbo); + glVertexAttribPointer(attributes[1], 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); + glEnableVertexAttribArray(1); + //glTexCoordPointer(2, GL_FLOAT, 0, 0); + + /* XXX: this could be made optional, but since alpha is a constant throughout the stage + * integration I'm just always turning it on so stage_set_alpha() always works. There + * are definitely full-screen full-opaque shader node situations where the pointless + * performance hit sucks though, especially on older hardware. + */ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if (shader_node->uniforms_func) + shader_node->uniforms_func(shader_node->shader, shader_node->uniforms_ctxt, render_ctxt, n_uniforms, uniforms, shader_node->transform, alpha); + + glDrawArrays(GL_TRIANGLES, 0, 6); + glUseProgram(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + + +static void shader_node_free(const stage_t *stage, void *object) +{ + shader_node_t *shader_node = object; + + assert(stage); + assert(shader_node); + + /* XXX FIXME: hmm, maybe the caller should supply a shader_t ** instead */ + (void) shader_free(shader_node->shader); + free(shader_node); +} + + +static const stage_ops_t shader_node_ops = { + .render_func = shader_node_render, + .free_func = shader_node_free, +}; + +/* return a new shader stage node from an already compiled and linked shader program */ +stage_t * shader_node_new_shader(const stage_conf_t *conf, shader_t *shader, const m4f_t *transform, shader_node_uniforms_func_t *uniforms_func, void *uniforms_ctxt) +{ + shader_node_t *shader_node; + stage_t *stage; + + assert(conf); + assert(shader); + + if (!vbo) { + /* common to all shader nodes */ + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glGenBuffers(1, &tcbo); + glBindBuffer(GL_ARRAY_BUFFER, tcbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(texcoords), texcoords, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + shader_node = calloc(1, sizeof(shader_node_t)); + fatal_if(!shader_node, "Unable to allocate shader_node"); + + shader_ref(shader); + shader_node->shader = shader; + shader_node->uniforms_func = uniforms_func; + shader_node->uniforms_ctxt = uniforms_ctxt; + shader_node->transform = transform; + + stage = stage_new(conf, &shader_node_ops, shader_node); + fatal_if(!stage, "Unable to create stage \"%s\"", conf->name); + + return stage; +} + + +/* return a new shader stage node from source */ +stage_t * shader_node_new_src(const stage_conf_t *conf, const char *vs_src, const char *fs_src, const m4f_t *transform, shader_node_uniforms_func_t *uniforms_func, void *uniforms_ctxt, unsigned n_uniforms, const char **uniforms) +{ + stage_t *stage; + shader_t *shader; + + assert(vs_src); + assert(fs_src); + + shader = shader_pair_new(vs_src, fs_src, n_uniforms, uniforms, NELEMS(shader_node_attributes), shader_node_attributes); + stage = shader_node_new_shader(conf, shader, transform, uniforms_func, uniforms_ctxt); + shader_free(shader); + + return stage; +} + + +stage_t * shader_node_new_files(const stage_conf_t *conf, const char *vs_path, const char *fs_path, const m4f_t *transform, shader_node_uniforms_func_t *uniforms_func, void *uniforms_ctxt, unsigned n_uniforms, const char **uniforms) +{ + stage_t *stage; + shader_t *shader; + + assert(vs_path); + assert(fs_path); + + shader = shader_pair_new_files(vs_path, fs_path, n_uniforms, uniforms, NELEMS(shader_node_attributes), shader_node_attributes); + stage = shader_node_new_shader(conf, shader, transform, uniforms_func, uniforms_ctxt); + shader_free(shader); + + return stage; +} -- cgit v1.2.3