From ca761a910c25197b10aeadcb0120dae132442d56 Mon Sep 17 00:00:00 2001 From: Tomasz Kramkowski Date: Mon, 26 Mar 2018 16:38:09 +0100 Subject: init commit --- .gitignore | 14 ++ LICENSE | 20 +++ Makefile | 33 ++++ frag.glsl | 9 + gldefs.h | 53 ++++++ gltest.c | 143 ++++++++++++++++ linmath.h | 562 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ loadgl.c | 100 +++++++++++ loadgl.h | 56 ++++++ shaders.h | 20 +++ vert.glsl | 9 + 11 files changed, 1019 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 frag.glsl create mode 100644 gldefs.h create mode 100644 gltest.c create mode 100644 linmath.h create mode 100644 loadgl.c create mode 100644 loadgl.h create mode 100644 shaders.h create mode 100644 vert.glsl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d7216ac --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.o +gltest +*.exe +*.dll + +*.d + +tags +TAGS + +eprintf.h +eprintf.c + +config.mk diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1a71eb5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright 2018 Tomasz Kramkowski + +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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..df93833 --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +# Copyright (C) 2018 Tomasz Kramkowski +# SPDX-License-Identifier: MIT +-include config.mk + +PROG := gltest + +EPRINTF_PATH ?= ../eprintf +PKG_CONFIG ?= pkg-config +LN ?= ln -sf + +LIBS := glfw3 +CPPFLAGS += -D__gl_h_ +CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS)) -std=c11 -MMD -MP +LDFLAGS += -Wl,--as-needed +LDLIBS += $(shell $(PKG_CONFIG) --libs $(LIBS)) + +OBJ := gltest.o loadgl.o eprintf.o vert.o frag.o +DEP := $(OBJ:.o=.d) + +%.o: %.glsl + $(LD) -r -b binary $(OUTPUT_OPTION) $< + +all: $(PROG) +$(PROG): $(OBJ) +clean: + $(RM) $(OBJ) $(DEP) $(PROG) + +include $(EPRINTF_PATH)/module.mk +deplinks: $(EPRINTF_FILES) + +-include $(DEP) + +.PHONY: all clean diff --git a/frag.glsl b/frag.glsl new file mode 100644 index 0000000..aa5ec0f --- /dev/null +++ b/frag.glsl @@ -0,0 +1,9 @@ +// Copyright (C) 2018 Tomasz Kramkowski +// SPDX-License-Identifier: MIT +#version 330 core +out vec4 color; + +void main() +{ + color = vec4(1.0f, 0.5f, 0.2f, 1.0f); +} diff --git a/gldefs.h b/gldefs.h new file mode 100644 index 0000000..429b85d --- /dev/null +++ b/gldefs.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 Tomasz Kramkowski + * SPDX-License-Identifier: MIT + */ +#ifndef GLDEFS_H +#define GLDEFS_H + +#include +#include + +typedef uint8_t GLboolean; +typedef int8_t GLbyte; +typedef uint8_t GLubyte; +typedef char GLchar; +typedef int16_t GLshort; +typedef uint16_t GLushort; +typedef int32_t GLint; +typedef uint32_t GLuint; +typedef int32_t GLfixed; +typedef int64_t GLint64; +typedef uint64_t GLuint64; +typedef int32_t GLsizei; +typedef int32_t GLenum; +typedef ptrdiff_t GLintptr; +typedef ptrdiff_t GLsizeiptr; +typedef struct gl_sync *GLsync; +typedef uint32_t GLbitfield; +typedef uint16_t GLhalf; +typedef float GLfloat; +typedef float GLclampf; +typedef double GLdouble; +typedef double GLclampd; + +enum { + GL_FALSE = 0, + GL_TRUE = 1, + GL_TRIANGLES = 0x0004, + GL_FRONT_AND_BACK = 0x0408, + GL_UNSIGNED_INT = 0x1405, + GL_FLOAT = 0x1406, + GL_LINE = 0x1B01, + GL_COLOR_BUFFER_BIT = 0x4000, + GL_ARRAY_BUFFER = 0x8892, + GL_ELEMENT_ARRAY_BUFFER = 0x8893, + GL_STREAM_DRAW = 0x88e0, + GL_STATIC_DRAW = 0x88e4, + GL_DYNAMIC_DRAW = 0x88e8, + GL_FRAGMENT_SHADER = 0x8b30, + GL_VERTEX_SHADER = 0x8b31, + GL_COMPILE_STATUS = 0x8b81, +}; + +#endif // GLDEFS_H diff --git a/gltest.c b/gltest.c new file mode 100644 index 0000000..e40ee0f --- /dev/null +++ b/gltest.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2018 Tomasz Kramkowski + * SPDX-License-Identifier: MIT + */ +#include +#include + +#include "eprintf.h" +#include "gldefs.h" +#include "linmath.h" +#include "loadgl.h" +#include "shaders.h" + +enum { + WIDTH = 800, + HEIGHT = 600, + LOGSIZE = 1024, +}; + +void fb_size_cb(GLFWwindow *win, int width, int height) +{ + (void)win; + gl_viewport(0, 0, width, height); +} + +void take_input(GLFWwindow *win) +{ + if (glfwGetKey(win, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(win, true); +} + +GLuint load_shader(GLenum type, const char *data, GLint size) +{ + GLuint shdr; + GLint success; + char log[LOGSIZE]; + + shdr = gl_shdr_create(type); + gl_shdr_source(shdr, 1, &data, &size); + gl_shdr_compile(shdr); + gl_shdr_param(shdr, GL_COMPILE_STATUS, &success); + + if (!success) { + gl_shdr_infolog(shdr, sizeof log, NULL, log); + eprintf("Failed to compile shader:\n%s", log); + } + + return shdr; +} + +GLuint load_prog(void) +{ + GLuint vert, frag, prog; + GLuint success; + char log[LOGSIZE]; + + vert = load_shader(GL_VERTEX_SHADER, shader_vert_data, shader_vert_size); + frag = load_shader(GL_FRAGMENT_SHADER, shader_frag_data, shader_frag_size); + prog = gl_prog_create(); + gl_prog_attachshdr(prog, vert); + gl_prog_attachshdr(prog, frag); + gl_prog_link(prog); + gl_prog_param(prog, GL_COMPILE_STATUS, &success); + if (!success) { + gl_prog_infolog(prog, sizeof log, NULL, log); + eprintf("Failed to link program:\n%s", log); + } + gl_shdr_del(vert); + gl_shdr_del(frag); + + return prog; +} + +int main(int argc, char **argv) +{ + GLFWwindow *win; + int ret; + GLuint ebo, vbo, vao, prog; + vec3 verts[] = { + { 0.5f, 0.5f, 0.0f }, + { 0.5f, -0.5f, 0.0f }, + { -0.5f, -0.5f, 0.0f }, + { -0.5f, 0.5f, 0.0f }, + }; + GLuint idxs[] = { + 0, 1, 3, + 1, 2, 3, + }; + + setprogname(argc >= 1 && argv[0] != NULL ? argv[0] : "gltest"); + + glfwInit(); + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + win = glfwCreateWindow(WIDTH, HEIGHT, "gltest", NULL, NULL); + if (win == NULL) + eprintf("Failed to create GLFW window"); + glfwMakeContextCurrent(win); + + ret = lgl_load((lgl_loadfunc *)glfwGetProcAddress); + if (ret != 0) + eprintf("Failed to load GL: %s", lgl_strerror(ret)); + glfwSetFramebufferSizeCallback(win, &fb_size_cb); + fb_size_cb(win, WIDTH, HEIGHT); + + gl_buf_gen(1, &vbo); + gl_buf_gen(1, &ebo); + gl_va_gen(1, &vao); + + gl_va_bind(vao); + + gl_buf_bind(GL_ARRAY_BUFFER, vbo); + gl_buf_data(GL_ARRAY_BUFFER, sizeof verts, verts, GL_STATIC_DRAW); + + gl_buf_bind(GL_ELEMENT_ARRAY_BUFFER, ebo); + gl_buf_data(GL_ELEMENT_ARRAY_BUFFER, sizeof idxs, idxs, GL_STATIC_DRAW); + + gl_va_define(0, 3, GL_FLOAT, GL_FALSE, sizeof *verts, 0); + gl_va_enable(0); + + prog = load_prog(); + + gl_poly_mode(GL_FRONT_AND_BACK, GL_LINE); + + while (!glfwWindowShouldClose(win)) { + take_input(win); + + gl_clearcolor(0.2, 0.3, 0.3, 1.0); + gl_clear(GL_COLOR_BUFFER_BIT); + gl_prog_use(prog); + gl_va_bind(vao); + gl_draw_elems(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + gl_va_bind(0); + + glfwSwapBuffers(win); + glfwPollEvents(); + } + + glfwTerminate(); +} diff --git a/linmath.h b/linmath.h new file mode 100644 index 0000000..8d9ff8c --- /dev/null +++ b/linmath.h @@ -0,0 +1,562 @@ +/* + * Copyright (C) 2013 Wolfgang 'datenwolf' Draxinger + * Copyright (C) 2018 Tomasz Kramkowski + * SPDX-License-Identifier: WTFPL + */ +#ifndef LINMATH_H +#define LINMATH_H + +#include + +typedef float lm_elem; +#define LINMATH_H_DEFINE_VEC(n) \ +typedef lm_elem vec##n[n]; \ +static inline void vec##n##_add(vec##n r, vec##n const a, vec##n const b) \ +{ \ + for (int i = 0; i < n; ++i) \ + r[i] = a[i] + b[i]; \ +} \ +static inline void vec##n##_sub(vec##n r, vec##n const a, vec##n const b) \ +{ \ + for (int i = 0; i < n; ++i) \ + r[i] = a[i] - b[i]; \ +} \ +static inline void vec##n##_scale(vec##n r, vec##n const v, lm_elem const s) \ +{ \ + for (int i = 0; i < n; ++i) \ + r[i] = v[i] * s; \ +} \ +static inline lm_elem vec##n##_mul_inner(vec##n const a, vec##n const b) \ +{ \ + lm_elem p = 0.0; \ + for (int i = 0; i < n; ++i) \ + p += b[i] * a[i]; \ + return p; \ +} \ +static inline lm_elem vec##n##_len(vec##n const v) \ +{ \ + return sqrtf(vec##n##_mul_inner(v, v)); \ +} \ +static inline void vec##n##_norm(vec##n r, vec##n const v) \ +{ \ + lm_elem k = 1.0 / vec##n##_len(v); \ + vec##n##_scale(r, v, k); \ +} \ +static inline void vec##n##_min(vec##n r, vec##n a, vec##n b) \ +{ \ + for (int i = 0; i < n; ++i) \ + r[i] = a[i] < b[i] ? a[i] : b[i]; \ +} \ +static inline void vec##n##_max(vec##n r, vec##n a, vec##n b) \ +{ \ + for (int i = 0; i < n; ++i) \ + r[i] = a[i] > b[i] ? a[i] : b[i]; \ +} + +LINMATH_H_DEFINE_VEC(2) +LINMATH_H_DEFINE_VEC(3) +LINMATH_H_DEFINE_VEC(4) + +static inline void vec3_mul_cross(vec3 r, vec3 const a, vec3 const b) +{ + r[0] = a[1] * b[2] - a[2] * b[1]; + r[1] = a[2] * b[0] - a[0] * b[2]; + r[2] = a[0] * b[1] - a[1] * b[0]; +} + +static inline void vec3_reflect(vec3 r, vec3 const v, vec3 const n) +{ + lm_elem p = 2.0f * vec3_mul_inner(v, n); + for (int i = 0; i < 3; ++i) + r[i] = v[i] - p * n[i]; +} + +static inline void vec4_mul_cross(vec4 r, vec4 a, vec4 b) +{ + r[0] = a[1] * b[2] - a[2] * b[1]; + r[1] = a[2] * b[0] - a[0] * b[2]; + r[2] = a[0] * b[1] - a[1] * b[0]; + r[3] = 1.0f; +} + +static inline void vec4_reflect(vec4 r, vec4 v, vec4 n) +{ + lm_elem p = 2.0f * vec4_mul_inner(v, n); + for (int i = 0; i < 4; ++i) + r[i] = v[i] - p * n[i]; +} + +typedef vec4 mat4x4[4]; +static inline void mat4x4_identity(mat4x4 M) +{ + for (int i = 0; i < 4; ++i) + for (int j = 0; j < 4; ++j) + M[i][j] = i == j ? 1.0f : 0.0f; +} +static inline void mat4x4_dup(mat4x4 M, mat4x4 N) +{ + for (int i = 0; i < 4; ++i) + for (int j = 0; j < 4; ++j) + M[i][j] = N[i][j]; +} +static inline void mat4x4_row(vec4 r, mat4x4 M, int i) +{ + for (int k = 0; k < 4; ++k) + r[k] = M[k][i]; +} +static inline void mat4x4_col(vec4 r, mat4x4 M, int i) +{ + for (int k = 0; k < 4; ++k) + r[k] = M[i][k]; +} +static inline void mat4x4_transpose(mat4x4 M, mat4x4 N) +{ + for (int j = 0; j < 4; ++j) + for (int i = 0; i < 4; ++i) + M[i][j] = N[j][i]; +} +static inline void mat4x4_add(mat4x4 M, mat4x4 a, mat4x4 b) +{ + for (int i = 0; i < 4; ++i) + vec4_add(M[i], a[i], b[i]); +} +static inline void mat4x4_sub(mat4x4 M, mat4x4 a, mat4x4 b) +{ + for (int i = 0; i < 4; ++i) + vec4_sub(M[i], a[i], b[i]); +} +static inline void mat4x4_scale(mat4x4 M, mat4x4 a, lm_elem k) +{ + for (int i = 0; i < 4; ++i) + vec4_scale(M[i], a[i], k); +} +static inline void mat4x4_scale_aniso(mat4x4 M, mat4x4 a, lm_elem x, lm_elem y, lm_elem z) +{ + vec4_scale(M[0], a[0], x); + vec4_scale(M[1], a[1], y); + vec4_scale(M[2], a[2], z); + for (int i = 0; i < 4; ++i) + M[3][i] = a[3][i]; +} +static inline void mat4x4_mul(mat4x4 M, mat4x4 a, mat4x4 b) +{ + mat4x4 temp; + for (int c = 0; c < 4; ++c) { + for (int r = 0; r < 4; ++r) { + temp[c][r] = 0.0f; + for (int k = 0; k < 4; ++k) + temp[c][r] += a[k][r] * b[c][k]; + } + } + mat4x4_dup(M, temp); +} +static inline void mat4x4_mul_vec4(vec4 r, mat4x4 M, vec4 v) +{ + for (int j = 0; j < 4; ++j) { + r[j] = 0.0f; + for (int i = 0; i < 4; ++i) + r[j] += M[i][j] * v[i]; + } +} +static inline void mat4x4_translate(mat4x4 T, lm_elem x, lm_elem y, lm_elem z) +{ + mat4x4_identity(T); + T[3][0] = x; + T[3][1] = y; + T[3][2] = z; +} +static inline void mat4x4_translate_in_place(mat4x4 M, lm_elem x, lm_elem y, lm_elem z) +{ + vec4 t = {x, y, z, 0}; + vec4 r; + for (int i = 0; i < 4; ++i) { + mat4x4_row(r, M, i); + M[3][i] += vec4_mul_inner(r, t); + } +} +static inline void mat4x4_from_vec3_mul_outer(mat4x4 M, vec3 a, vec3 b) +{ + for (int i = 0; i < 4; ++i) + for (int j = 0; j < 4; ++j) + M[i][j] = i < 3 && j < 3 ? a[i] * b[j] : 0.0f; +} +static inline void mat4x4_rotate(mat4x4 R, mat4x4 M, lm_elem x, lm_elem y, lm_elem z, lm_elem angle) +{ + lm_elem s = sinf(angle); + lm_elem c = cosf(angle); + vec3 u = {x, y, z}; + + if (vec3_len(u) > 1e-4) { + vec3_norm(u, u); + mat4x4 T; + mat4x4_from_vec3_mul_outer(T, u, u); + + mat4x4 S = { + { 0, u[2], -u[1], 0}, + {-u[2], 0, u[0], 0}, + { u[1], -u[0], 0, 0}, + { 0, 0, 0, 0} + }; + mat4x4_scale(S, S, s); + + mat4x4 C; + mat4x4_identity(C); + mat4x4_sub(C, C, T); + + mat4x4_scale(C, C, c); + + mat4x4_add(T, T, C); + mat4x4_add(T, T, S); + + T[3][3] = 1.; + mat4x4_mul(R, M, T); + } else { + mat4x4_dup(R, M); + } +} +static inline void mat4x4_rotate_X(mat4x4 Q, mat4x4 M, lm_elem angle) +{ + lm_elem s = sinf(angle); + lm_elem c = cosf(angle); + mat4x4 R = { + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, c, s, 0.0f}, + {0.0f, -s, c, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f} + }; + mat4x4_mul(Q, M, R); +} +static inline void mat4x4_rotate_Y(mat4x4 Q, mat4x4 M, lm_elem angle) +{ + lm_elem s = sinf(angle); + lm_elem c = cosf(angle); + mat4x4 R = { + { c, 0.0f, s, 0.0f}, + { 0.0f, 1.0f, 0.0f, 0.0f}, + { -s, 0.0f, c, 0.0f}, + { 0.0f, 0.0f, 0.0f, 1.0f} + }; + mat4x4_mul(Q, M, R); +} +static inline void mat4x4_rotate_Z(mat4x4 Q, mat4x4 M, lm_elem angle) +{ + lm_elem s = sinf(angle); + lm_elem c = cosf(angle); + mat4x4 R = { + { c, s, 0.0f, 0.0f}, + { -s, c, 0.0f, 0.0f}, + { 0.0f, 0.0f, 1.0f, 0.0f}, + { 0.0f, 0.0f, 0.0f, 1.0f} + }; + mat4x4_mul(Q, M, R); +} +static inline void mat4x4_invert(mat4x4 T, mat4x4 M) +{ + lm_elem s[6]; + lm_elem c[6]; + s[0] = M[0][0] * M[1][1] - M[1][0] * M[0][1]; + s[1] = M[0][0] * M[1][2] - M[1][0] * M[0][2]; + s[2] = M[0][0] * M[1][3] - M[1][0] * M[0][3]; + s[3] = M[0][1] * M[1][2] - M[1][1] * M[0][2]; + s[4] = M[0][1] * M[1][3] - M[1][1] * M[0][3]; + s[5] = M[0][2] * M[1][3] - M[1][2] * M[0][3]; + + c[0] = M[2][0] * M[3][1] - M[3][0] * M[2][1]; + c[1] = M[2][0] * M[3][2] - M[3][0] * M[2][2]; + c[2] = M[2][0] * M[3][3] - M[3][0] * M[2][3]; + c[3] = M[2][1] * M[3][2] - M[3][1] * M[2][2]; + c[4] = M[2][1] * M[3][3] - M[3][1] * M[2][3]; + c[5] = M[2][2] * M[3][3] - M[3][2] * M[2][3]; + + /* Assumes it is invertible */ + lm_elem idet = 1.0f / (s[0] * c[5] - s[1] * c[4] + + s[2] * c[3] + s[3] * c[2] + - s[4] * c[1] + s[5] * c[0]); + + T[0][0] = ( M[1][1] * c[5] - M[1][2] * c[4] + M[1][3] * c[3]) * idet; + T[0][1] = (-M[0][1] * c[5] + M[0][2] * c[4] - M[0][3] * c[3]) * idet; + T[0][2] = ( M[3][1] * s[5] - M[3][2] * s[4] + M[3][3] * s[3]) * idet; + T[0][3] = (-M[2][1] * s[5] + M[2][2] * s[4] - M[2][3] * s[3]) * idet; + + T[1][0] = (-M[1][0] * c[5] + M[1][2] * c[2] - M[1][3] * c[1]) * idet; + T[1][1] = ( M[0][0] * c[5] - M[0][2] * c[2] + M[0][3] * c[1]) * idet; + T[1][2] = (-M[3][0] * s[5] + M[3][2] * s[2] - M[3][3] * s[1]) * idet; + T[1][3] = ( M[2][0] * s[5] - M[2][2] * s[2] + M[2][3] * s[1]) * idet; + + T[2][0] = ( M[1][0] * c[4] - M[1][1] * c[2] + M[1][3] * c[0]) * idet; + T[2][1] = (-M[0][0] * c[4] + M[0][1] * c[2] - M[0][3] * c[0]) * idet; + T[2][2] = ( M[3][0] * s[4] - M[3][1] * s[2] + M[3][3] * s[0]) * idet; + T[2][3] = (-M[2][0] * s[4] + M[2][1] * s[2] - M[2][3] * s[0]) * idet; + + T[3][0] = (-M[1][0] * c[3] + M[1][1] * c[1] - M[1][2] * c[0]) * idet; + T[3][1] = ( M[0][0] * c[3] - M[0][1] * c[1] + M[0][2] * c[0]) * idet; + T[3][2] = (-M[3][0] * s[3] + M[3][1] * s[1] - M[3][2] * s[0]) * idet; + T[3][3] = ( M[2][0] * s[3] - M[2][1] * s[1] + M[2][2] * s[0]) * idet; +} +static inline void mat4x4_orthonormalize(mat4x4 R, mat4x4 M) +{ + mat4x4_dup(R, M); + lm_elem s = 1.; + vec3 h; + + vec3_norm(R[2], R[2]); + + s = vec3_mul_inner(R[1], R[2]); + vec3_scale(h, R[2], s); + vec3_sub(R[1], R[1], h); + vec3_norm(R[2], R[2]); + + s = vec3_mul_inner(R[1], R[2]); + vec3_scale(h, R[2], s); + vec3_sub(R[1], R[1], h); + vec3_norm(R[1], R[1]); + + s = vec3_mul_inner(R[0], R[1]); + vec3_scale(h, R[1], s); + vec3_sub(R[0], R[0], h); + vec3_norm(R[0], R[0]); +} + +static inline void mat4x4_frustum(mat4x4 M, lm_elem l, lm_elem r, lm_elem b, lm_elem t, lm_elem n, lm_elem f) +{ + M[0][0] = 2.0f * n/(r-l); + M[0][1] = M[0][2] = M[0][3] = 0.0f; + + M[1][1] = 2.0 * n/(t-b); + M[1][0] = M[1][2] = M[1][3] = 0.0f; + + M[2][0] = (r+l)/(r-l); + M[2][1] = (t+b)/(t-b); + M[2][2] = -(f+n)/(f-n); + M[2][3] = -1.0f; + + M[3][2] = -2.0f * (f * n)/(f-n); + M[3][0] = M[3][1] = M[3][3] = 0.0f; +} +static inline void mat4x4_ortho(mat4x4 M, lm_elem l, lm_elem r, lm_elem b, lm_elem t, lm_elem n, lm_elem f) +{ + M[0][0] = 2.0f/(r-l); + M[0][1] = M[0][2] = M[0][3] = 0.0f; + + M[1][1] = 2.0f/(t-b); + M[1][0] = M[1][2] = M[1][3] = 0.0f; + + M[2][2] = -2.0f/(f-n); + M[2][0] = M[2][1] = M[2][3] = 0.0f; + + M[3][0] = -(r+l)/(r-l); + M[3][1] = -(t+b)/(t-b); + M[3][2] = -(f+n)/(f-n); + M[3][3] = 1.0f; +} +static inline void mat4x4_perspective(mat4x4 m, lm_elem y_fov, lm_elem aspect, lm_elem n, lm_elem f) +{ + lm_elem const a = 1.0f / tan(y_fov / 2.0f); + + m[0][0] = a / aspect; + m[0][1] = 0.0f; + m[0][2] = 0.0f; + m[0][3] = 0.0f; + + m[1][0] = 0.0f; + m[1][1] = a; + m[1][2] = 0.0f; + m[1][3] = 0.0f; + + m[2][0] = 0.0f; + m[2][1] = 0.0f; + m[2][2] = -((f + n) / (f - n)); + m[2][3] = -1.0f; + + m[3][0] = 0.0f; + m[3][1] = 0.0f; + m[3][2] = -((2.0f * f * n) / (f - n)); + m[3][3] = 0.0f; +} +static inline void mat4x4_look_at(mat4x4 m, vec3 eye, vec3 center, vec3 up) +{ + /* Adapted from Android's OpenGL Matrix.java. */ + /* See the OpenGL GLUT documentation for gluLookAt for a description */ + /* of the algorithm. We implement it in a straightforward way: */ + + /* TODO: The negation of of can be spared by swapping the order of + * operands in the following cross products in the right way. */ + vec3 f; + vec3_sub(f, center, eye); + vec3_norm(f, f); + + vec3 s; + vec3_mul_cross(s, f, up); + vec3_norm(s, s); + + vec3 t; + vec3_mul_cross(t, s, f); + + m[0][0] = s[0]; + m[0][1] = t[0]; + m[0][2] = -f[0]; + m[0][3] = 0.0f; + + m[1][0] = s[1]; + m[1][1] = t[1]; + m[1][2] = -f[1]; + m[1][3] = 0.0f; + + m[2][0] = s[2]; + m[2][1] = t[2]; + m[2][2] = -f[2]; + m[2][3] = 0.0f; + + m[3][0] = 0.0f; + m[3][1] = 0.0f; + m[3][2] = 0.0f; + m[3][3] = 1.0f; + + mat4x4_translate_in_place(m, -eye[0], -eye[1], -eye[2]); +} + +typedef lm_elem quat[4]; +static inline void quat_identity(quat q) +{ + q[0] = q[1] = q[2] = 0.0f; + q[3] = 1.0f; +} +static inline void quat_add(quat r, quat a, quat b) +{ + for (int i = 0; i < 4; ++i) + r[i] = a[i] + b[i]; +} +static inline void quat_sub(quat r, quat a, quat b) +{ + for (int i = 0; i < 4; ++i) + r[i] = a[i] - b[i]; +} +static inline void quat_mul(quat r, quat p, quat q) +{ + vec3 w; + vec3_mul_cross(r, p, q); + vec3_scale(w, p, q[3]); + vec3_add(r, r, w); + vec3_scale(w, q, p[3]); + vec3_add(r, r, w); + r[3] = p[3]*q[3] - vec3_mul_inner(p, q); +} +static inline void quat_scale(quat r, quat v, lm_elem s) +{ + for (int i = 0; i < 4; ++i) + r[i] = v[i] * s; +} +static inline lm_elem quat_inner_product(quat a, quat b) +{ + lm_elem p = 0.0f; + for (int i = 0; i < 4; ++i) + p += b[i]*a[i]; + return p; +} +static inline void quat_conj(quat r, quat q) +{ + for (int i = 0; i < 3; ++i) + r[i] = -q[i]; + r[3] = q[3]; +} +static inline void quat_rotate(quat r, lm_elem angle, vec3 axis) { + vec3 v; + vec3_scale(v, axis, sinf(angle / 2)); + for (int i = 0; i < 3; ++i) + r[i] = v[i]; + r[3] = cosf(angle / 2); +} +#define quat_norm vec4_norm +static inline void quat_mul_vec3(vec3 r, quat q, vec3 v) +{ +/* + * Method by Fabian 'ryg' Giessen (of Farbrausch) +t = 2 * cross(q.xyz, v) +v' = v + q.w * t + cross(q.xyz, t) + */ + vec3 t; + vec3 q_xyz = { q[0], q[1], q[2] }; + vec3 u = { q[0], q[1], q[2] }; + + vec3_mul_cross(t, q_xyz, v); + vec3_scale(t, t, 2); + + vec3_mul_cross(u, q_xyz, t); + vec3_scale(t, t, q[3]); + + vec3_add(r, v, t); + vec3_add(r, r, u); +} +static inline void mat4x4_from_quat(mat4x4 M, quat q) +{ + lm_elem a = q[3]; + lm_elem b = q[0]; + lm_elem c = q[1]; + lm_elem d = q[2]; + lm_elem a2 = a * a; + lm_elem b2 = b * b; + lm_elem c2 = c * c; + lm_elem d2 = d * d; + + M[0][0] = a2 + b2 - c2 - d2; + M[0][1] = 2.0f * (b * c + a * d); + M[0][2] = 2.0f * (b * d - a * c); + M[0][3] = 0.0f; + + M[1][0] = 2 * (b * c - a * d); + M[1][1] = a2 - b2 + c2 - d2; + M[1][2] = 2.0f * (c * d + a * b); + M[1][3] = 0.0f; + + M[2][0] = 2.0f * (b * d + a * c); + M[2][1] = 2.0f * (c * d - a * b); + M[2][2] = a2 - b2 - c2 + d2; + M[2][3] = 0.0f; + + M[3][0] = M[3][1] = M[3][2] = 0.0f; + M[3][3] = 1.0f; +} + +static inline void mat4x4o_mul_quat(mat4x4 R, mat4x4 M, quat q) +{ +/* XXX: The way this is written only works for othogonal matrices. */ +/* TODO: Take care of non-orthogonal case. */ + quat_mul_vec3(R[0], q, M[0]); + quat_mul_vec3(R[1], q, M[1]); + quat_mul_vec3(R[2], q, M[2]); + + R[3][0] = R[3][1] = R[3][2] = 0.0f; + R[3][3] = 1.0f; +} + +static inline void quat_from_mat4x4(quat q, mat4x4 M) +{ + lm_elem r = 0.0f; + int i; + + int perm[] = { 0, 1, 2, 0, 1 }; + int *p = perm; + + for (int i = 0; i < 3; i++) { + lm_elem m = M[i][i]; + if (m < r) + continue; + m = r; + p = &perm[i]; + } + + r = sqrtf(1.0f + M[p[0]][p[0]] - M[p[1]][p[1]] - M[p[2]][p[2]]); + + if (r < 1e-6) { + q[0] = 1.0f; + q[1] = q[2] = q[3] = 0.0f; + return; + } + + q[0] = r/2.0f; + q[1] = (M[p[0]][p[1]] - M[p[1]][p[0]])/(2.0f * r); + q[2] = (M[p[2]][p[0]] - M[p[0]][p[2]])/(2.0f * r); + q[3] = (M[p[2]][p[1]] - M[p[1]][p[2]])/(2.0f * r); +} + +#endif // LINMATH_H diff --git a/loadgl.c b/loadgl.c new file mode 100644 index 0000000..0dbd720 --- /dev/null +++ b/loadgl.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2018 Tomasz Kramkowski + * SPDX-License-Identifier: MIT + */ +#include + +#include "loadgl.h" + +#define LGL_FUNC(name, glname) \ + name##_func *name; \ + static const char *name##_gln = #glname; + +LGL_FUNC(gl_viewport, glViewport); +LGL_FUNC(gl_clearcolor, glClearColor); +LGL_FUNC(gl_clear, glClear); +LGL_FUNC(gl_buf_gen, glGenBuffers); +LGL_FUNC(gl_buf_bind, glBindBuffer); +LGL_FUNC(gl_buf_data, glBufferData); +LGL_FUNC(gl_shdr_create, glCreateShader); +LGL_FUNC(gl_shdr_del, glDeleteShader); +LGL_FUNC(gl_shdr_source, glShaderSource); +LGL_FUNC(gl_shdr_compile, glCompileShader); +LGL_FUNC(gl_shdr_param, glGetShaderiv); +LGL_FUNC(gl_shdr_infolog, glGetShaderInfoLog); +LGL_FUNC(gl_prog_create, glCreateProgram); +LGL_FUNC(gl_prog_attachshdr, glAttachShader); +LGL_FUNC(gl_prog_link, glLinkProgram); +LGL_FUNC(gl_prog_use, glUseProgram); +LGL_FUNC(gl_prog_del, glDeleteProgram); +LGL_FUNC(gl_prog_param, glGetProgramiv); +LGL_FUNC(gl_prog_infolog, glGetProgramInfoLog); +LGL_FUNC(gl_va_define, glVertexAttribPointer); +LGL_FUNC(gl_va_enable, glEnableVertexAttribArray); +LGL_FUNC(gl_va_disable, glDisableVertexAttribArray); +LGL_FUNC(gl_va_gen, glGenVertexArrays); +LGL_FUNC(gl_va_bind, glBindVertexArray); +LGL_FUNC(gl_draw_arrays, glDrawArrays); +LGL_FUNC(gl_draw_elems, glDrawElements); +LGL_FUNC(gl_poly_mode, glPolygonMode); + +static void *load_func(const char *name, lgl_loadfunc *load, jmp_buf env) +{ + void *ret; + + ret = load(name); + if (ret == NULL) + longjmp(env, LGL_MISSING); + + return ret; +} + +enum lgl_status lgl_load(lgl_loadfunc *load) +{ + enum lgl_status status; + jmp_buf env; + + status = setjmp(env); + if (status != 0) + return status; + +#define LGL_LOAD(name) name = (name##_func *)load_func(name##_gln, load, env); + LGL_LOAD(gl_viewport); + LGL_LOAD(gl_clearcolor); + LGL_LOAD(gl_clear); + LGL_LOAD(gl_buf_gen); + LGL_LOAD(gl_buf_bind); + LGL_LOAD(gl_buf_data); + LGL_LOAD(gl_shdr_create); + LGL_LOAD(gl_shdr_del); + LGL_LOAD(gl_shdr_source); + LGL_LOAD(gl_shdr_compile); + LGL_LOAD(gl_shdr_param); + LGL_LOAD(gl_shdr_infolog); + LGL_LOAD(gl_prog_create); + LGL_LOAD(gl_prog_attachshdr); + LGL_LOAD(gl_prog_link); + LGL_LOAD(gl_prog_use); + LGL_LOAD(gl_prog_del); + LGL_LOAD(gl_prog_param); + LGL_LOAD(gl_prog_infolog); + LGL_LOAD(gl_va_define); + LGL_LOAD(gl_va_enable); + LGL_LOAD(gl_va_disable); + LGL_LOAD(gl_va_gen); + LGL_LOAD(gl_va_bind); + LGL_LOAD(gl_draw_arrays); + LGL_LOAD(gl_draw_elems); + LGL_LOAD(gl_poly_mode); + + return LGL_OK; +} + +const char *lgl_strerror(enum lgl_status status) +{ + switch (status) { + case LGL_OK: return "Success"; + case LGL_MISSING: return "Missing function"; + } + return "Unknown"; +} diff --git a/loadgl.h b/loadgl.h new file mode 100644 index 0000000..01acdfa --- /dev/null +++ b/loadgl.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018 Tomasz Kramkowski + * SPDX-License-Identifier: MIT + */ +#ifndef LOADGL_H +#define LOADGL_H + +#include "gldefs.h" + +typedef void *lgl_loadfunc(const char *name); + +enum lgl_status { + LGL_OK, + LGL_MISSING, +}; + +enum lgl_status lgl_load(lgl_loadfunc *load); +const char *lgl_strerror(enum lgl_status status); + +#define _LGL_INTERFACE(rett, name, func, ...) \ + typedef rett func(__VA_ARGS__); \ + extern func *name; +#define LGL_INTERFACE(rett, name, ...) _LGL_INTERFACE(rett, name, name##_func, __VA_ARGS__) + +LGL_INTERFACE(void, gl_viewport, GLint x, GLint y, GLsizei width, GLsizei height); +LGL_INTERFACE(void, gl_clearcolor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +LGL_INTERFACE(void, gl_clear, GLbitfield mask); +/* LGL_INTERFACE(GLboolean, gl_isbuf, GLuint buffer); */ +LGL_INTERFACE(void, gl_buf_gen, GLsizei n, GLuint *buffers); +/* LGL_INTERFACE(void, gl_buf_del, GLsizei n, GLuint *buffers); */ +LGL_INTERFACE(void, gl_buf_bind, GLenum target, GLuint buffer); +LGL_INTERFACE(void, gl_buf_data, GLenum target, GLsizeiptr size, const void *data, GLenum usage); +LGL_INTERFACE(GLuint, gl_shdr_create, GLenum type); +LGL_INTERFACE(void, gl_shdr_del, GLuint shader); +/* LGL_INTERFACE(GLboolean, gl_isshdr, GLuint shader); */ +LGL_INTERFACE(void, gl_shdr_source, GLuint shader, GLsizei count, const char * const *string, const GLint *length); +LGL_INTERFACE(void, gl_shdr_compile, GLuint shader); +LGL_INTERFACE(void, gl_shdr_param, GLuint shader, GLenum pname, GLint *params); +LGL_INTERFACE(void, gl_shdr_infolog, GLuint shader, GLsizei size, GLsizei *len, char *data); +LGL_INTERFACE(GLuint, gl_prog_create, void); +LGL_INTERFACE(void, gl_prog_del, GLuint program); +LGL_INTERFACE(void, gl_prog_attachshdr, GLuint program, GLuint shader); +LGL_INTERFACE(void, gl_prog_link, GLuint program); +LGL_INTERFACE(void, gl_prog_use, GLuint program); +LGL_INTERFACE(void, gl_prog_param, GLuint prog, GLenum pname, GLint *params); +LGL_INTERFACE(void, gl_prog_infolog, GLuint prog, GLsizei size, GLsizei *len, char *data); +LGL_INTERFACE(void, gl_va_define, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *data); +LGL_INTERFACE(void, gl_va_enable, GLuint index); +LGL_INTERFACE(void, gl_va_disable, GLuint index); +LGL_INTERFACE(void, gl_va_gen, GLsizei n, GLuint *arrays); +LGL_INTERFACE(void, gl_va_bind, GLuint va); +LGL_INTERFACE(void, gl_draw_arrays, GLenum mode, GLint first, GLsizei count); +LGL_INTERFACE(void, gl_draw_elems, GLenum mode, GLsizei count, GLenum type, const void *indices); +LGL_INTERFACE(void, gl_poly_mode, GLenum face, GLenum mode); + +#endif // LOADGL_H diff --git a/shaders.h b/shaders.h new file mode 100644 index 0000000..4a388e5 --- /dev/null +++ b/shaders.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2018 Tomasz Kramkowski + * SPDX-License-Identifier: MIT + */ +#ifndef SHADERS_H +#define SHADERS_H + +#include + +extern char _binary_vert_glsl_start[]; +extern char _binary_vert_glsl_end[]; +#define shader_vert_data _binary_vert_glsl_start +#define shader_vert_size (_binary_vert_glsl_end - _binary_vert_glsl_start) + +extern char _binary_frag_glsl_start[]; +extern char _binary_frag_glsl_end[]; +#define shader_frag_data _binary_frag_glsl_start +#define shader_frag_size (_binary_frag_glsl_end - _binary_frag_glsl_start) + +#endif // SHADERS_H diff --git a/vert.glsl b/vert.glsl new file mode 100644 index 0000000..1730d35 --- /dev/null +++ b/vert.glsl @@ -0,0 +1,9 @@ +// Copyright (C) 2018 Tomasz Kramkowski +// SPDX-License-Identifier: MIT +#version 330 core +layout (location = 0) in vec3 pos; + +void main() +{ + gl_Position = vec4(pos, 1.0); +} -- cgit v1.2.3-54-g00ecf