summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Herrmann <dh.herrmann@googlemail.com>2012-03-23 12:50:35 +0100
committerDavid Herrmann <dh.herrmann@googlemail.com>2012-03-23 12:50:35 +0100
commit25f41f84f784f5a1aae612a5824bfa4aa84ecf40 (patch)
treeebc40ea6fe1cdd40e79d5b526625e6f8b0f64c38 /src
parent50e0cdbec805e4685847f1424950446d59a2412a (diff)
gl: add GL subsystem
The GL subsystem is copied from the old output_context subsystem and now provides shader and GL-math. It will replace the old output_context subs soon. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
Diffstat (limited to 'src')
-rw-r--r--src/gl.h93
-rw-r--r--src/gl_math.c304
-rw-r--r--src/gl_shader.c365
3 files changed, 762 insertions, 0 deletions
diff --git a/src/gl.h b/src/gl.h
new file mode 100644
index 0000000..5bf9694
--- /dev/null
+++ b/src/gl.h
@@ -0,0 +1,93 @@
+/*
+ * GL - Graphics Layer
+ *
+ * Copyright (c) 2011-2012 David Herrmann <dh.herrmann@googlemail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Graphics Layer
+ * This provides lots of helpers to work with OpenGL APIs. This includes
+ * math-helpers, basic shaders and a texture-API. If working with this API,
+ * there must always be a valid OpenGL-context!
+ */
+
+#ifndef GL_GL_H
+#define GL_GL_H
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+/* miscellaneous */
+void gl_clear_error();
+bool gl_has_error();
+
+/*
+ * Math Helpers
+ * The gl_m4 type is a 4x4 matrix of floats. The gl_m4_stack is a stack of m4
+ * matrices where you can only access the top-most member.
+ */
+
+struct gl_m4_stack;
+
+void gl_m4_identity(float *m);
+void gl_m4_copy(float *dest, const float *src);
+void gl_m4_mult_dest(float *dest, const float *n, const float *m);
+void gl_m4_mult(float *n, const float *m);
+void gl_m4_translate(float *m, float x, float y, float z);
+void gl_m4_scale(float *m, float x, float y, float z);
+void gl_m4_transpose_dest(float *dest, const float *src);
+void gl_m4_transpose(float *m);
+
+int gl_m4_stack_new(struct gl_m4_stack **out);
+void gl_m4_stack_free(struct gl_m4_stack *stack);
+float *gl_m4_stack_push(struct gl_m4_stack *stack);
+float *gl_m4_stack_pop(struct gl_m4_stack *stack);
+float *gl_m4_stack_tip(struct gl_m4_stack *stack);
+
+/*
+ * Texture API
+ * This allows to create new textures which can then be used to draw images with
+ * the shader API.
+ */
+
+unsigned int gl_tex_new();
+void gl_tex_free(unsigned int tex);
+void gl_tex_load(unsigned int tex, unsigned int width,
+ unsigned int height, void *buf);
+
+/*
+ * Shader API
+ */
+
+struct gl_shader;
+
+int gl_shader_new(struct gl_shader **out);
+void gl_shader_ref(struct gl_shader *shader);
+void gl_shader_unref(struct gl_shader *shader);
+
+void gl_shader_draw_def(struct gl_shader *shader, float *vertices,
+ float *colors, size_t num);
+void gl_shader_draw_tex(struct gl_shader *shader, const float *vertices,
+ const float *texcoords, size_t num,
+ unsigned int tex, const float *m);
+
+#endif /* GL_GL_H */
diff --git a/src/gl_math.c b/src/gl_math.c
new file mode 100644
index 0000000..5cc40e8
--- /dev/null
+++ b/src/gl_math.c
@@ -0,0 +1,304 @@
+/*
+ * GL - Graphics Layer
+ *
+ * Copyright (c) 2011 David Herrmann <dh.herrmann@googlemail.com>
+ * Copyright (c) 2011 University of Tuebingen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Math Helper
+ * Basic linear algebra.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "gl.h"
+
+struct gl_m4_entry {
+ struct gl_m4_entry *next;
+ float matrix[16];
+};
+
+struct gl_m4_stack {
+ struct gl_m4_entry stack;
+ struct gl_m4_entry *cache;
+};
+
+void gl_m4_identity(float *m)
+{
+ if (!m)
+ return;
+
+ m[0] = 1;
+ m[1] = 0;
+ m[2] = 0;
+ m[3] = 0;
+
+ m[4] = 0;
+ m[5] = 1;
+ m[6] = 0;
+ m[7] = 0;
+
+ m[8] = 0;
+ m[9] = 0;
+ m[10] = 1;
+ m[11] = 0;
+
+ m[12] = 0;
+ m[13] = 0;
+ m[14] = 0;
+ m[15] = 1;
+}
+
+void gl_m4_copy(float *dest, const float *src)
+{
+ if (!dest || !src)
+ return;
+
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+ dest[3] = src[3];
+
+ dest[4] = src[4];
+ dest[5] = src[5];
+ dest[6] = src[6];
+ dest[7] = src[7];
+
+ dest[8] = src[8];
+ dest[9] = src[9];
+ dest[10] = src[10];
+ dest[11] = src[11];
+
+ dest[12] = src[12];
+ dest[13] = src[13];
+ dest[14] = src[14];
+ dest[15] = src[15];
+}
+
+/* Matrix Multiplication
+ * This is the simplest algorithm to multiply two matrices. It computes:
+ * dest = n * m
+ * That is, n is left side and m is right side.
+ *
+ * Matrix-multiplication is heavy, avoid it if possible.
+ */
+void gl_m4_mult_dest(float *dest, const float *n, const float *m)
+{
+ unsigned int row, col, j;
+
+ if (!dest || !n || !m)
+ return;
+
+ for (row = 0; row < 4; ++row) {
+ for (col = 0; col < 4; ++col) {
+ dest[row * 4 + col] = 0;
+ for (j = 0; j < 4; ++j)
+ dest[row * 4 + col] +=
+ n[row * 4 + j] * m[j * 4 + col];
+ }
+ }
+}
+
+/* this computes n = n * m */
+void gl_m4_mult(float *n, const float *m)
+{
+ float tmp[16];
+
+ if (!n || !m)
+ return;
+
+ gl_m4_mult_dest(tmp, n, m);
+ gl_m4_copy(n, tmp);
+}
+
+void gl_m4_translate(float *m, float x, float y, float z)
+{
+ float trans[16] = { 1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1 };
+
+ if (!m)
+ return;
+
+ gl_m4_mult(m, trans);
+}
+
+void gl_m4_scale(float *m, float x, float y, float z)
+{
+ float scale[16] = { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 };
+
+ if (!m)
+ return;
+
+ gl_m4_mult(m, scale);
+}
+
+void gl_m4_transpose_dest(float *dest, const float *src)
+{
+ if (!dest || !src)
+ return;
+
+ dest[0] = src[0];
+ dest[5] = src[5];
+ dest[10] = src[10];
+ dest[15] = src[15];
+
+ dest[1] = src[4];
+ dest[4] = src[1];
+
+ dest[8] = src[2];
+ dest[2] = src[8];
+
+ dest[3] = src[12];
+ dest[12] = src[3];
+
+ dest[7] = src[13];
+ dest[13] = src[7];
+
+ dest[11] = src[14];
+ dest[14] = src[11];
+
+ dest[6] = src[9];
+ dest[9] = src[6];
+}
+
+void gl_m4_transpose(float *m)
+{
+ float tmp;
+
+ if (!m)
+ return;
+
+ tmp = m[1];
+ m[1] = m[4];
+ m[4] = tmp;
+
+ tmp = m[8];
+ m[8] = m[2];
+ m[2] = tmp;
+
+ tmp = m[3];
+ m[3] = m[12];
+ m[12] = tmp;
+
+ tmp = m[7];
+ m[7] = m[13];
+ m[13] = tmp;
+
+ tmp = m[11];
+ m[11] = m[14];
+ m[14] = tmp;
+
+ tmp = m[6];
+ m[6] = m[9];
+ m[9] = tmp;
+}
+
+int gl_m4_stack_new(struct gl_m4_stack **out)
+{
+ struct gl_m4_stack *stack;
+
+ if (!out)
+ return -EINVAL;
+
+ stack = malloc(sizeof(*stack));
+ if (!stack)
+ return -ENOMEM;
+
+ memset(stack, 0, sizeof(*stack));
+ gl_m4_identity(stack->stack.matrix);
+
+ *out = stack;
+ return 0;
+}
+
+void gl_m4_stack_free(struct gl_m4_stack *stack)
+{
+ struct gl_m4_entry *tmp;
+
+ if (!stack)
+ return;
+
+ while (stack->stack.next) {
+ tmp = stack->stack.next;
+ stack->stack.next = tmp->next;
+ free(tmp);
+ }
+
+ while (stack->cache) {
+ tmp = stack->cache;
+ stack->cache = tmp->next;
+ free(tmp);
+ }
+
+ free(stack);
+}
+
+float *gl_m4_stack_push(struct gl_m4_stack *stack)
+{
+ struct gl_m4_entry *entry;
+
+ if (stack->cache) {
+ entry = stack->cache;
+ stack->cache = entry->next;
+ } else {
+ entry = malloc(sizeof(*entry));
+ if (!entry)
+ return NULL;
+ }
+
+ gl_m4_copy(entry->matrix, stack->stack.matrix);
+ entry->next = stack->stack.next;
+ stack->stack.next = entry;
+
+ return stack->stack.matrix;
+}
+
+float *gl_m4_stack_pop(struct gl_m4_stack *stack)
+{
+ struct gl_m4_entry *entry;
+
+ if (!stack)
+ return NULL;
+
+ entry = stack->stack.next;
+ if (!entry) {
+ gl_m4_identity(stack->stack.matrix);
+ return stack->stack.matrix;
+ }
+
+ stack->stack.next = entry->next;
+ entry->next = stack->cache;
+ stack->cache = entry;
+
+ gl_m4_copy(stack->stack.matrix, entry->matrix);
+
+ return stack->stack.matrix;
+}
+
+float *gl_m4_stack_tip(struct gl_m4_stack *stack)
+{
+ if (!stack)
+ return NULL;
+
+ return stack->stack.matrix;
+}
diff --git a/src/gl_shader.c b/src/gl_shader.c
new file mode 100644
index 0000000..d10ccd4
--- /dev/null
+++ b/src/gl_shader.c
@@ -0,0 +1,365 @@
+/*
+ * GL - Graphics Layer
+ *
+ * Copyright (c) 2011 David Herrmann <dh.herrmann@googlemail.com>
+ * Copyright (c) 2011 University of Tuebingen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Shader API
+ * This provides basic shader objects that are used to draw sprites and
+ * textures.
+ */
+
+#define GL_GLEXT_PROTOTYPES
+
+#include <errno.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include "gl.h"
+#include "output.h"
+#include "log.h"
+
+#define LOG_SUBSYSTEM "gl"
+
+/* Clear the GL error stack. The standard says that the error value is just a
+ * single value and no list/stack. However, multiple error fields may be defined
+ * and glGetError() returns only one of them until all are cleared. Hence, we
+ * loop until no more error is retrieved.
+ */
+void gl_clear_error()
+{
+ GLenum err;
+
+ do {
+ err = glGetError();
+ } while (err != GL_NO_ERROR);
+}
+
+/* return true if there is a pending GL error */
+bool gl_has_error()
+{
+ GLenum err;
+
+ err = glGetError();
+ if (err != GL_NO_ERROR) {
+ log_err("GL error %d", err);
+ return true;
+ }
+
+ return false;
+}
+
+unsigned int gl_tex_new()
+{
+ GLuint tex = 0;
+
+ glGenTextures(1, &tex);
+ glBindTexture(GL_TEXTURE_2D, tex);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ return tex;
+}
+
+void gl_tex_free(unsigned int tex)
+{
+ glDeleteTextures(1, &tex);
+}
+
+void gl_tex_load(unsigned int tex, unsigned int width,
+ unsigned int height, void *buf)
+{
+ if (!buf || !width || !height)
+ return;
+
+ /* With OpenGL instead of OpenGLES2 we must use this on linux:
+ * glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA,
+ * GL_UNSIGNED_BYTE, buf);
+ */
+
+ glBindTexture(GL_TEXTURE_2D, tex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, width, height, 0,
+ GL_BGRA_EXT, GL_UNSIGNED_BYTE, buf);
+}
+
+struct gl_shader {
+ unsigned long ref;
+
+ GLuint def_program;
+ GLuint def_vshader;
+ GLuint def_fshader;
+ GLuint def_uni_projection;
+
+ GLuint tex_program;
+ GLuint tex_vshader;
+ GLuint tex_fshader;
+ GLuint tex_uni_projection;
+ GLuint tex_uni_texture;
+};
+
+/* external shader sources; generated during build */
+extern const char *kmscon_vert_def;
+extern const char *kmscon_frag_def;
+extern const char *kmscon_vert_tex;
+extern const char *kmscon_frag_tex;
+
+static int compile_shader(GLenum type, const char *source)
+{
+ char msg[512];
+ GLint status = 1;
+ GLuint s;
+
+ s = glCreateShader(type);
+ if (s == GL_NONE) {
+ log_warn("cannot allocate GL shader");
+ return GL_NONE;
+ }
+
+ glShaderSource(s, 1, &source, NULL);
+ glCompileShader(s);
+
+ glGetShaderiv(s, GL_COMPILE_STATUS, &status);
+ if (status == GL_FALSE) {
+ msg[0] = 0;
+ glGetShaderInfoLog(s, sizeof(msg), NULL, msg);
+ log_warn("cannot compile shader: %s", msg);
+ return GL_NONE;
+ }
+
+ return s;
+}
+
+static int init_def_shader(struct gl_shader *shader)
+{
+ char msg[512];
+ GLint status = 1;
+ int ret;
+
+ shader->def_vshader = compile_shader(GL_VERTEX_SHADER,
+ kmscon_vert_def);
+ if (shader->def_vshader == GL_NONE)
+ return -EFAULT;
+
+ shader->def_fshader = compile_shader(GL_FRAGMENT_SHADER,
+ kmscon_frag_def);
+ if (shader->def_fshader == GL_NONE) {
+ ret = -EFAULT;
+ goto err_vshader;
+ }
+
+ shader->def_program = glCreateProgram();
+ glAttachShader(shader->def_program, shader->def_vshader);
+ glAttachShader(shader->def_program, shader->def_fshader);
+ glBindAttribLocation(shader->def_program, 0, "position");
+ glBindAttribLocation(shader->def_program, 1, "color");
+
+ glLinkProgram(shader->def_program);
+ glGetProgramiv(shader->def_program, GL_LINK_STATUS, &status);
+ if (status == GL_FALSE) {
+ msg[0] = 0;
+ glGetProgramInfoLog(shader->def_program, sizeof(msg),
+ NULL, msg);
+ log_warn("cannot link shader: %s", msg);
+ ret = -EFAULT;
+ goto err_link;
+ }
+
+ shader->def_uni_projection =
+ glGetUniformLocation(shader->def_program, "projection");
+
+ return 0;
+
+err_link:
+ glDeleteProgram(shader->def_program);
+ glDeleteShader(shader->def_fshader);
+err_vshader:
+ glDeleteShader(shader->def_vshader);
+ return ret;
+}
+
+static void free_def_shader(struct gl_shader *shader)
+{
+ glDeleteProgram(shader->def_program);
+ glDeleteShader(shader->def_fshader);
+ glDeleteShader(shader->def_vshader);
+}
+
+static int init_tex_shader(struct gl_shader *shader)
+{
+ char msg[512];
+ GLint status = 1;
+ int ret;
+
+ shader->tex_vshader = compile_shader(GL_VERTEX_SHADER,
+ kmscon_vert_tex);
+ if (shader->tex_vshader == GL_NONE)
+ return -EFAULT;
+
+ shader->tex_fshader = compile_shader(GL_FRAGMENT_SHADER,
+ kmscon_frag_tex);
+ if (shader->tex_fshader == GL_NONE) {
+ ret = -EFAULT;
+ goto err_vshader;
+ }
+
+ shader->tex_program = glCreateProgram();
+ glAttachShader(shader->tex_program, shader->tex_vshader);
+ glAttachShader(shader->tex_program, shader->tex_fshader);
+ glBindAttribLocation(shader->tex_program, 0, "position");
+ glBindAttribLocation(shader->tex_program, 1, "texture_position");
+
+ glLinkProgram(shader->tex_program);
+ glGetProgramiv(shader->tex_program, GL_LINK_STATUS, &status);
+ if (status == GL_FALSE) {
+ msg[0] = 0;
+ glGetProgramInfoLog(shader->tex_program, sizeof(msg),
+ NULL, msg);
+ log_warn("cannot link shader: %s", msg);
+ ret = -EFAULT;
+ goto err_link;
+ }
+
+ shader->tex_uni_projection =
+ glGetUniformLocation(shader->tex_program, "projection");
+ shader->tex_uni_texture =
+ glGetUniformLocation(shader->tex_program, "texture");
+
+ return 0;
+
+err_link:
+ glDeleteProgram(shader->tex_program);
+ glDeleteShader(shader->tex_fshader);
+err_vshader:
+ glDeleteShader(shader->tex_vshader);
+ return ret;
+}
+
+static void free_tex_shader(struct gl_shader *shader)
+{
+ glDeleteProgram(shader->tex_program);
+ glDeleteShader(shader->tex_fshader);
+ glDeleteShader(shader->tex_vshader);
+}
+
+int gl_shader_new(struct gl_shader **out)
+{
+ struct gl_shader *shader;
+ int ret;
+
+ if (!out)
+ return -EINVAL;
+
+ shader = malloc(sizeof(*shader));
+ if (!shader)
+ return -ENOMEM;
+ memset(shader, 0, sizeof(*shader));
+ shader->ref = 1;
+
+ ret = init_def_shader(shader);
+ if (ret)
+ goto err_free;
+
+ ret = init_tex_shader(shader);
+ if (ret)
+ goto err_def;
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ *out = shader;
+ return 0;
+
+err_def:
+ free_def_shader(shader);
+err_free:
+ free(shader);
+ return ret;
+}
+
+void gl_shader_ref(struct gl_shader *shader)
+{
+ if (!shader || !shader->ref)
+ return;
+
+ ++shader->ref;
+}
+
+void gl_shader_unref(struct gl_shader *shader)
+{
+ if (!shader || !shader->ref || --shader->ref)
+ return;
+
+ free_tex_shader(shader);
+ free_def_shader(shader);
+ free(shader);
+}
+
+void gl_shader_draw_def(struct gl_shader *shader, float *vertices,
+ float *colors, size_t num)
+{
+ float m[16] = { 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1 };
+
+ if (!shader || !vertices || !colors || !num)
+ return;
+
+ glUseProgram(shader->def_program);
+ glUniformMatrix4fv(shader->def_uni_projection, 1, GL_FALSE, m);
+
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
+ glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, colors);
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+ glDrawArrays(GL_TRIANGLES, 0, num);
+}
+
+void gl_shader_draw_tex(struct gl_shader *shader, const float *vertices,
+ const float *texcoords, size_t num,
+ unsigned int tex, const float *m)
+{
+ float mat[16];
+
+ if (!shader || !vertices || !texcoords || !num || !m)
+ return;
+
+ gl_m4_transpose_dest(mat, m);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, tex);
+
+ glUseProgram(shader->tex_program);
+ glUniformMatrix4fv(shader->tex_uni_projection, 1, GL_FALSE, mat);
+ glUniform1i(shader->tex_uni_texture, 0);
+
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texcoords);
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+ glDrawArrays(GL_TRIANGLES, 0, num);
+}