commit 7d3ccccd8dac4ddc2a16ff1091c5ada21b214ebc
parent 6111393aa6512bd135046590cafb5a75d48761e0
Author: amin <dev@aminmesbah.com>
Date: Mon, 24 Jun 2019 00:27:55 +0000
Merge branch 'features/wasm-without-emscripten'
FossilOrigin-Name: 33c8e27955bc1d2bc0c00a5a0d2867d3e5075404ec80e9b2b65a61ce0c5a061e
Diffstat:
22 files changed, 1141 insertions(+), 721 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,6 +1,9 @@
*.out
*.swp
-build/*
+*.bc
+*.o
+
+out/*
tags
diff --git a/Makefile b/Makefile
@@ -2,27 +2,27 @@ CC = clang
CFLAGS = -std=c99 -Ilib -Wall -Wextra -Wshadow -Wswitch-enum -Wno-unused-parameter -Wno-missing-braces
LDFLAGS = -ldl -lglfw -lGL
-SRC_FILES = game.c glad.c glmth.c platform_linux.c shader.c
+SRC_FILES = platform_linux.c
SRC = $(addprefix src/, $(SRC_FILES))
EXE_FILE = quaternion_demo
-LIB_FILES = game.c glad.c glmth.c shader.c
+LIB_FILES = game.c
LIB = $(addprefix src/, $(LIB_FILES))
LIB_NAME = game.so
-DBGDIR = build/debug
+DBGDIR = out/debug
DBGEXE = $(DBGDIR)/$(EXE_FILE)
DBGCFLAGS = -g -Og -Werror
-RELDIR = build/release
+RELDIR = out/release
RELEXE = $(RELDIR)/$(EXE_FILE)
RELLIB = $(RELDIR)/$(LIB_NAME)
RELLIBTMP = $(RELLIB).tmp
RELCFLAGS = -DPLATFORM_HOTLOAD_GAME_CODE -O2 -Os
-EMS_FILES = game.c glmth.c platform_emscripten.c shader.c
+EMS_FILES = platform_emscripten.c
EMSSRC = $(addprefix src/, $(EMS_FILES))
-EMSDIR = build/emscripten
+EMSDIR = out/emscripten
EMSEXE = $(EMSDIR)/$(EXE_FILE).html
EMSCFLAGS = --preload-file shader -s USE_GLFW=3 -s USE_WEBGL2=1
@@ -46,7 +46,7 @@ clean:
rm -f $(RELDIR)/* $(DBGDIR)/*
debug: build_debug
- cgdb $(DBGEXE)
+ gdb $(DBGEXE)
dir_debug:
@mkdir -p $(DBGDIR)
diff --git a/build_wasm.sh b/build_wasm.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+
+set -e # fail if any command has a non-zero exit status
+set -u # fail if any undefined variable is referenced
+set -o pipefail # propagate failure exit status through a pipeline
+shopt -s globstar nullglob # enable recursive and null globbing
+
+out_dir="./out"
+wasm_dir="${out_dir}/wasm"
+
+mkdir -p $wasm_dir
+cp src/platform_wasm_loader.js $wasm_dir/script.js
+cp src/platform_wasm_index.html $wasm_dir/index.html
+cp -r shader/ $wasm_dir
+
+clang \
+ -cc1 \
+ -Ofast \
+ -emit-llvm-bc \
+ -triple=wasm32-unknown-unknown-unknown-wasm \
+ -ffreestanding \
+ -fno-builtin \
+ -std=c11 \
+ -DGAME_WEBGL \
+ src/platform_wasm.c
+
+llvm-link -o $wasm_dir/wasm.bc src/*.bc
+opt -O3 -disable-simplify-libcalls $wasm_dir/wasm.bc -o $wasm_dir/wasm.bc
+llc -O3 -disable-simplify-libcalls -filetype=obj $wasm_dir/wasm.bc -o $wasm_dir/wasm.o
+
+wasm-ld \
+ --no-entry $wasm_dir/wasm.o \
+ -o $wasm_dir/binary.wasm \
+ -allow-undefined-file src/platform_wasm_js_symbols.txt \
+ --export-all \
+ --import-memory
+
+rm $wasm_dir/*.o
+rm $wasm_dir/*.bc
+rm src/*.bc
diff --git a/shader/pyramid_f.glsl b/shader/pyramid_f.glsl
@@ -1,12 +0,0 @@
-#version 300 es
-
-precision highp float;
-
-uniform vec3 pyramid_color;
-
-out vec4 frag_color;
-
-void main()
-{
- frag_color = vec4(pyramid_color, 1.0f);
-}
diff --git a/shader/pyramid_v.glsl b/shader/pyramid_v.glsl
@@ -1,12 +0,0 @@
-#version 300 es
-
-layout (location = 0) in vec3 a_position;
-
-uniform mat4 model;
-uniform mat4 view;
-uniform mat4 projection;
-
-void main()
-{
- gl_Position = projection * view * model * vec4(a_position, 1.0f);
-}
diff --git a/src/game.c b/src/game.c
@@ -1,55 +1,16 @@
#include "game.h"
-#include <assert.h>
-
-// TODO: remove references to emscripten
-#ifdef __EMSCRIPTEN__
-#include <GLES3/gl3.h>
-#else
-#include "glad/glad.h"
+#ifndef GAME_WEBGL
+#include "glad.c"
#endif
+#include "shader.c"
-#ifdef PLATFORM_HOTLOAD_GAME_CODE
-void game_load_opengl_symbols(void)
-{
- gladLoadGL();
-}
-#endif
-
-
-float wrap(float n, float min, float max)
-{
- if (n > max)
- {
- return min;
- }
- else if (n < min)
- {
- return max;
- }
- else
- {
- return n;
- }
-}
-
-
-float randf(float min, float max)
-{
- assert(min < max);
- float random = ((float) rand()) / (float) RAND_MAX;
- float diff = max - min;
- float r = random * diff;
- return min + r;
-}
-
-
-void game_init(struct GameState *game_state, uint32_t screen_width, uint32_t screen_height)
+void game_init(struct GameState *game_state, u32 screen_width, u32 screen_height)
{
// load cube vertex data
{
- GLfloat cube_vertices[] = {
+ f32 cube_vertices[] = {
// positions // colors
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
@@ -61,7 +22,7 @@ void game_init(struct GameState *game_state, uint32_t screen_width, uint32_t scr
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f,
};
- GLuint elements[] = {
+ u32 elements[] = {
0, 4, 6, 6, 2, 0,
1, 5, 7, 7, 3, 1,
3, 2, 0, 0, 1, 3,
@@ -70,11 +31,11 @@ void game_init(struct GameState *game_state, uint32_t screen_width, uint32_t scr
2, 6, 7, 7, 3, 2,
};
- GLuint cube_vao;
+ u32 cube_vao;
glGenVertexArrays(1, &cube_vao);
glBindVertexArray(cube_vao);
- GLuint cube_vbo;
+ u32 cube_vbo;
glGenBuffers(1, &cube_vbo);
glBindBuffer(GL_ARRAY_BUFFER, cube_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(cube_vertices), cube_vertices, GL_STATIC_DRAW);
@@ -86,31 +47,39 @@ void game_init(struct GameState *game_state, uint32_t screen_width, uint32_t scr
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(*cube_vertices), (GLvoid*)(3 * sizeof(*cube_vertices)));
glEnableVertexAttribArray(1);
- GLuint cube_ebo;
+ u32 cube_ebo;
glGenBuffers(1, &cube_ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cube_ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
- glBindVertexArray(0);
game_state->cube_vao = cube_vao;
game_state->cube_vbo = cube_vbo;
game_state->cube_ebo = cube_ebo;
}
- game_state->pyramid_shader = shader_compile("shader/pyramid_v.glsl", "shader/pyramid_f.glsl");
- game_state->cube_shader = shader_compile("shader/cube_v.glsl", "shader/cube_f.glsl");
+ // game_shader_load
+ {
+ struct PlatformApi platform = game_state->platform;
+ char *v_source = platform.platform_read_entire_file("shader/cube_v.glsl");
+ char *f_source = platform.platform_read_entire_file("shader/cube_f.glsl");
+ print = platform.platform_print;
+ struct Shader main_shader = {0};
+ // TODO: Check this result
+ shader_compile(v_source, f_source, &main_shader);
+ platform.platform_memory_free(v_source);
+ platform.platform_memory_free(f_source);
+ game_state->cube_shader = main_shader;
+ }
- glEnable(GL_DEPTH_TEST);
+ //glEnable(GL_DEPTH_TEST);
- // TODO: remove references to emscripten
-#ifndef __EMSCRIPTEN__
- glEnable(GL_MULTISAMPLE);
+#ifndef GAME_WEBGL
+ //glEnable(GL_MULTISAMPLE);
#endif
}
-
-void game_update_and_render(struct GameState *game_state, float dt, uint32_t screen_width, uint32_t screen_height)
+void game_update_and_render(struct GameState *game_state, float dt, u32 screen_width, u32 screen_height)
{
glDepthMask(GL_TRUE);
glClearColor(0.2f, 0.2f, 0.3f, 1.0f);
@@ -123,31 +92,6 @@ void game_update_and_render(struct GameState *game_state, float dt, uint32_t scr
view = glmth_translate(view, glmth_v3_init(0.0f, 0.0f, -3.0f));
projection = glmth_projection_perspective_fov(glmth_rad(45.0f), (float)screen_width / (float)screen_height, 0.1f, 100.0f);
-#if 0
- // render pyramid
- {
- shader_use(&game_state->pyramid_shader);
- shader_setm4(&game_state->pyramid_shader, "view", &view);
- shader_setm4(&game_state->pyramid_shader, "projection", &projection);
-
- m4 model = glmth_m4_init_id();
- f32 angle = 20.0f;
- model = glmth_rotate(model, dt * glmth_rad(angle), glmth_v3_init(0.5f, 1.0f, 0.0f));
-
- f32 color_freq = dt * 0.1f;
- v3 pyramid_color = glmth_v3_init(
- glmth_sinf(color_freq),
- glmth_sinf(color_freq + (2 * M_PI / 3)),
- glmth_sinf(color_freq + (4 * M_PI / 3)));
- shader_setm4(&game_state->pyramid_shader, "model", &model);
- shader_setv3(&game_state->pyramid_shader, "pyramid_color", &pyramid_color);
-
- glBindVertexArray(game_state->pyramid_vao_id);
- glDrawElements(GL_TRIANGLES, 18, GL_UNSIGNED_INT, 0);
- glBindVertexArray(0);
- }
-#endif
-
// render cube
{
// Depth testing causes the blending to be applied inconsistently
@@ -175,19 +119,23 @@ void game_update_and_render(struct GameState *game_state, float dt, uint32_t scr
shader_setm4(&game_state->cube_shader, "model", &model);
shader_setf(&game_state->cube_shader, "alpha", alpha);
- glBindVertexArray(game_state->cube_vao);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
- glBindVertexArray(0);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
}
}
-
void game_cleanup(struct GameState *game_state)
{
- glDeleteVertexArrays(1, &game_state->pyramid_vao_id);
- glDeleteBuffers(1, &game_state->pyramid_vbo_id);
- glDeleteBuffers(1, &game_state->pyramid_ebo_id);
+ glDeleteVertexArrays(1, &game_state->cube_vao);
+ glDeleteBuffers(1, &game_state->cube_vbo);
+ glDeleteBuffers(1, &game_state->cube_ebo);
+}
+
+#ifdef PLATFORM_HOTLOAD_GAME_CODE
+void game_load_opengl_symbols(void)
+{
+ gladLoadGL();
}
+#endif
diff --git a/src/game.h b/src/game.h
@@ -1,41 +1,44 @@
-#ifndef GAME_H
-#define GAME_H
+#pragma once
-// TODO: remove references to emscripten
-#ifdef __EMSCRIPTEN__
-#include <GLES3/gl3.h>
+#include <stddef.h>
+#include <stdint.h>
+#define static_assert _Static_assert
+
+#define NULL ((void*)0)
+
+// TODO: fix this
+#define assert(x) (void)0
+
+typedef _Bool bool;
+#define true 1
+#define false 0
+
+#ifdef GAME_WEBGL
+#include "webgl.h"
#else
#include "glad/glad.h"
#endif
#include "glmth.h"
#include "shader.h"
+#include "platform.h"
-
-struct GameState
+void *memcpy(void *dst, const void *src, size_t n)
{
- GLuint pyramid_vao_id;
- GLuint pyramid_vbo_id;
- GLuint pyramid_ebo_id;
- GLuint cube_vao;
- GLuint cube_vbo;
- GLuint cube_ebo;
- struct Shader pyramid_shader;
- struct Shader cube_shader;
-};
-
-#ifdef PLATFORM_HOTLOAD_GAME_CODE
-// We need to call this from the platform layer in order for the game, when
-// built as a shared object library to have access to the OpenGL symbols.
-// https://github.com/Dav1dde/glad/issues/151
-typedef void (game_load_opengl_symbols_func)(void);
-void game_load_opengl_symbols(void);
-#endif // PLATFORM_HOTLOAD_GAME_CODE
-
-typedef void (game_update_and_render_func)(struct GameState *game_state, float dt, uint32_t screen_width, uint32_t screen_height);
-void game_update_and_render(struct GameState *game_state, float dt, uint32_t screen_width, uint32_t screen_height);
-
-void game_init(struct GameState *game_state, uint32_t screen_width, uint32_t screen_height);
-void game_cleanup(struct GameState *game_state);
+ u8 *destination = (u8 *)dst;
+ u8 *source = (u8 *)src;
-#endif
+ while (n--) {
+ *destination = *source;
+ destination++;
+ source++;
+ }
+
+ return dst;
+}
+
+// TODO: Consider finding a better way to make this func available
+static platform_print_func* print;
+
+void game_init(struct GameState *game_state, u32 screen_width, u32 screen_height);
+void game_cleanup(struct GameState *game_state);
diff --git a/src/glmth.c b/src/glmth.c
@@ -1,373 +0,0 @@
-#include "glmth.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-
-m4 glmth_m4_init_id()
-{
- m4 m = { 0 };
- m.E[0][0] = 1.0f;
- m.E[1][1] = 1.0f;
- m.E[2][2] = 1.0f;
- m.E[3][3] = 1.0f;
- return m;
-}
-
-
-void glmth_m4_print(m4 m)
-{
- printf("[\n");
- printf(" %f, %f, %f, %f\n", m.E[0][0], m.E[0][1], m.E[0][2], m.E[0][3]);
- printf(" %f, %f, %f, %f\n", m.E[1][0], m.E[1][1], m.E[1][2], m.E[1][3]);
- printf(" %f, %f, %f, %f\n", m.E[2][0], m.E[2][1], m.E[2][2], m.E[2][3]);
- printf(" %f, %f, %f, %f\n", m.E[3][0], m.E[3][1], m.E[3][2], m.E[3][3]);
- printf("]\n");
-}
-
-
-f32 *glmth_m4_valueptr(m4 m)
-{
- f32 *values = malloc(sizeof(m4));
- for (u8 v = 0; v < 16; ++v)
- {
- u8 row = v / 4;
- u8 col = v % 4;
- values[v] = m.E[row][col];
- }
- return values;
-}
-
-
-bool glmth_m4m4_eq(m4 mat1, m4 mat2)
-{
- for (u8 i = 0; i < 4; ++i)
- {
- for (u8 j = 0; j < 4; ++j)
- {
- if (mat1.E[i][j] != mat2.E[i][j])
- {
- return false;
- }
- }
- }
- return true;
-}
-
-
-m4 glmth_m4m4_m(m4 mat1, m4 mat2)
-{
- m4 r = {
- .E[0][0] = (mat1.E[0][0] * mat2.E[0][0]) + (mat1.E[0][1] * mat2.E[1][0]) + (mat1.E[0][2] * mat2.E[2][0]) + (mat1.E[0][3] * mat2.E[3][0]),
- .E[0][1] = (mat1.E[0][0] * mat2.E[0][1]) + (mat1.E[0][1] * mat2.E[1][1]) + (mat1.E[0][2] * mat2.E[2][1]) + (mat1.E[0][3] * mat2.E[3][1]),
- .E[0][2] = (mat1.E[0][0] * mat2.E[0][2]) + (mat1.E[0][1] * mat2.E[1][2]) + (mat1.E[0][2] * mat2.E[2][2]) + (mat1.E[0][3] * mat2.E[3][2]),
- .E[0][3] = (mat1.E[0][0] * mat2.E[0][3]) + (mat1.E[0][1] * mat2.E[1][3]) + (mat1.E[0][2] * mat2.E[2][3]) + (mat1.E[0][3] * mat2.E[3][3]),
-
- .E[1][0] = (mat1.E[1][0] * mat2.E[0][0]) + (mat1.E[1][1] * mat2.E[1][0]) + (mat1.E[1][2] * mat2.E[2][0]) + (mat1.E[1][3] * mat2.E[3][0]),
- .E[1][1] = (mat1.E[1][0] * mat2.E[0][1]) + (mat1.E[1][1] * mat2.E[1][1]) + (mat1.E[1][2] * mat2.E[2][1]) + (mat1.E[1][3] * mat2.E[3][1]),
- .E[1][2] = (mat1.E[1][0] * mat2.E[0][2]) + (mat1.E[1][1] * mat2.E[1][2]) + (mat1.E[1][2] * mat2.E[2][2]) + (mat1.E[1][3] * mat2.E[3][2]),
- .E[1][3] = (mat1.E[1][0] * mat2.E[0][3]) + (mat1.E[1][1] * mat2.E[1][3]) + (mat1.E[1][2] * mat2.E[2][3]) + (mat1.E[1][3] * mat2.E[3][3]),
-
- .E[2][0] = (mat1.E[2][0] * mat2.E[0][0]) + (mat1.E[2][1] * mat2.E[1][0]) + (mat1.E[2][2] * mat2.E[2][0]) + (mat1.E[2][3] * mat2.E[3][0]),
- .E[2][1] = (mat1.E[2][0] * mat2.E[0][1]) + (mat1.E[2][1] * mat2.E[1][1]) + (mat1.E[2][2] * mat2.E[2][1]) + (mat1.E[2][3] * mat2.E[3][1]),
- .E[2][2] = (mat1.E[2][0] * mat2.E[0][2]) + (mat1.E[2][1] * mat2.E[1][2]) + (mat1.E[2][2] * mat2.E[2][2]) + (mat1.E[2][3] * mat2.E[3][2]),
- .E[2][3] = (mat1.E[2][0] * mat2.E[0][3]) + (mat1.E[2][1] * mat2.E[1][3]) + (mat1.E[2][2] * mat2.E[2][3]) + (mat1.E[2][3] * mat2.E[3][3]),
-
- .E[3][0] = (mat1.E[3][0] * mat2.E[0][0]) + (mat1.E[3][1] * mat2.E[1][0]) + (mat1.E[3][2] * mat2.E[2][0]) + (mat1.E[3][3] * mat2.E[3][0]),
- .E[3][1] = (mat1.E[3][0] * mat2.E[0][1]) + (mat1.E[3][1] * mat2.E[1][1]) + (mat1.E[3][2] * mat2.E[2][1]) + (mat1.E[3][3] * mat2.E[3][1]),
- .E[3][2] = (mat1.E[3][0] * mat2.E[0][2]) + (mat1.E[3][1] * mat2.E[1][2]) + (mat1.E[3][2] * mat2.E[2][2]) + (mat1.E[3][3] * mat2.E[3][2]),
- .E[3][3] = (mat1.E[3][0] * mat2.E[0][3]) + (mat1.E[3][1] * mat2.E[1][3]) + (mat1.E[3][2] * mat2.E[2][3]) + (mat1.E[3][3] * mat2.E[3][3]),
- };
- return r;
-}
-
-
-v4 glmth_m4v4_m(m4 m, v4 v)
-{
- v4 r = {
- .x = (m.E[0][0] * v.x) + (m.E[0][1] * v.y) + (m.E[0][2] * v.z) + (m.E[0][3] * v.w),
- .y = (m.E[1][0] * v.x) + (m.E[1][1] * v.y) + (m.E[1][2] * v.z) + (m.E[1][3] * v.w),
- .z = (m.E[2][0] * v.x) + (m.E[2][1] * v.y) + (m.E[2][2] * v.z) + (m.E[2][3] * v.w),
- .w = (m.E[3][0] * v.x) + (m.E[3][1] * v.y) + (m.E[3][2] * v.z) + (m.E[3][3] * v.w),
- };
- return r;
-}
-
-
-v3 glmth_v3_cross(v3 vec1, v3 vec2)
-{
- v3 r = {
- .x = (vec1.y * vec2.z) - (vec1.z * vec2.y),
- .y = (vec1.z * vec2.x) - (vec1.x * vec2.z),
- .z = (vec1.x * vec2.y) - (vec1.y * vec2.x),
- };
- return r;
-}
-
-
-v3 glmth_v3_init(f32 x, f32 y, f32 z)
-{
- v3 v = { .x = x, .y = y, .z = z };
- return v;
-}
-
-v3 glmth_v3_init_f(f32 f)
-{
- return glmth_v3_init(f, f, f);
-}
-
-v3 glmth_v3f_m(v3 v, f32 s)
-{
- v3 r = { .x = v.x * s, .y = v.y * s, .z = v.z * s };
- return r;
-}
-
-v3 glmth_v3_negate(v3 v)
-{
- v3 r = { .x = -v.x, .y = -v.y, .z = -v.z };
- return r;
-}
-
-void glmth_v3_print(v3 v)
-{
- printf("( %f, %f, %f )\n", v.x, v.y, v.z);
-}
-
-
-v3 glmth_v3_a(v3 vec1, v3 vec2)
-{
- v3 r = {
- .x = vec1.x + vec2.x,
- .y = vec1.y + vec2.y,
- .z = vec1.z + vec2.z
- };
- return r;
-}
-
-
-v3 glmth_v3_s(v3 vec1, v3 vec2)
-{
- return glmth_v3_a(vec1, glmth_v3_negate(vec2));
-}
-
-
-void glmth_v4_print(v4 v)
-{
- printf("( %f, %f, %f, %f )\n", v.x, v.y, v.z, v.w);
-}
-
-
-bool glmth_v4v4_eq(v4 vec1, v4 vec2)
-{
- for (u8 i = 0; i < 4; ++i)
- {
- if (vec1.E[i] != vec2.E[i])
- {
- return false;
- }
- }
- return true;
-}
-
-
-void glmth_clampf(float *f, float min, float max)
-{
- if (*f < min)
- {
- *f = min;
- }
- else if (*f > max)
- {
- *f = max;
- }
-}
-
-
-f32 glmth_deg(f32 rad)
-{
- return rad * (180.0f / M_PI);
-}
-
-
-float glmth_lerpf(float f, float min, float max)
-{
- assert(f >= 0.0f && f <= 1.0f);
- return (1.0f - f) * min + f * max;
-}
-
-
-f32 glmth_rad(f32 deg)
-{
- return deg * (M_PI / 180.0f);
-}
-
-
-m4 glmth_rotate_x(m4 m, f32 rad)
-{
- f32 c = glmth_cosf(rad);
- f32 s = glmth_sinf(rad);
-
- m4 r = glmth_m4_init_id();
-
- r.E[1][1] = c;
- r.E[1][2] = -s;
-
- r.E[2][1] = s;
- r.E[2][2] = c;
-
- return glmth_m4m4_m(m, r);
-}
-
-
-m4 glmth_rotate_y(m4 m, f32 rad)
-{
- f32 c = glmth_cosf(rad);
- f32 s = glmth_sinf(rad);
-
- m4 r = glmth_m4_init_id();
-
- r.E[0][0] = c;
- r.E[0][2] = s;
-
- r.E[2][0] = -s;
- r.E[2][2] = c;
-
- return glmth_m4m4_m(m, r);
-}
-
-
-m4 glmth_rotate_z(m4 m, f32 rad)
-{
- f32 c = glmth_cosf(rad);
- f32 s = glmth_sinf(rad);
-
- m4 r = glmth_m4_init_id();
-
- r.E[0][0] = c;
- r.E[0][1] = -s;
-
- r.E[1][0] = s;
- r.E[1][1] = c;
-
- return glmth_m4m4_m(m, r);
-}
-
-m4 glmth_rotate(m4 m, f32 rad, v3 normalized_axis)
-{
- v3 axis = normalized_axis;
-
- f32 c = glmth_cosf(rad);
- f32 s = glmth_sinf(rad);
-
- m4 r = glmth_m4_init_id();
-
- r.E[0][0] = c + ((axis.x * axis.x) * (1 - c));
- r.E[0][1] = (axis.x * axis.y * (1 - c)) - (axis.z * s);
- r.E[0][2] = (axis.x * axis.z * (1 - c)) + (axis.y * s);
-
- r.E[1][0] = (axis.y * axis.x * (1 - c)) + (axis.z * s);
- r.E[1][1] = c + ((axis.y * axis.y) * (1 - c));
- r.E[1][2] = (axis.y * axis.z * (1 - c)) - (axis.x * s);
-
- r.E[2][0] = (axis.z * axis.x * (1 - c)) - (axis.y * s);
- r.E[2][1] = (axis.z * axis.y * (1 - c)) + (axis.x * s);
- r.E[2][2] = c + ((axis.z * axis.z) * (1 - c));
-
- return glmth_m4m4_m(m, r);
-}
-
-
-m4 glmth_scale(m4 m, v3 v)
-{
- m4 r = glmth_m4_init_id();
- r.E[0][0] = v.x;
- r.E[1][1] = v.y;
- r.E[2][2] = v.z;
- return glmth_m4m4_m(m, r);
-}
-
-
-m4 glmth_translate(m4 m, v3 v)
-{
- m4 r = glmth_m4_init_id();
- r.E[0][3] = v.x;
- r.E[1][3] = v.y;
- r.E[2][3] = v.z;
- return glmth_m4m4_m(m, r);
-}
-
-
-m4 glmth_projection_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far)
-{
- assert(left != right);
- assert(bottom != top);
- assert(near != far);
-
- m4 r = glmth_m4_init_id();
-
- r.E[0][0] = 2.0f / (right - left);
- r.E[0][1] = 0.0f;
- r.E[0][2] = 0.0f;
- r.E[0][3] = -(right + left) / (right - left);
-
- r.E[1][0] = 0.0f;
- r.E[1][1] = 2.0f / (top - bottom);
- r.E[1][2] = 0.0f;
- r.E[1][3] = -(top + bottom) / (top - bottom);
-
- r.E[2][0] = 0.0f;
- r.E[2][1] = 0.0f;
- r.E[2][2] = -2.0f / (far - near);
- r.E[2][3] = -(far + near) / (far - near);
-
- r.E[3][0] = 0.0f;
- r.E[3][1] = 0.0f;
- r.E[3][2] = 0.0f;
- r.E[3][3] = 1.0f;
-
- return r;
-}
-
-
-m4 glmth_projection_perspective(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far)
-{
- assert(left != right);
- assert(bottom != top);
- assert(near != far);
-
- m4 r = glmth_m4_init_id();
-
- r.E[0][0] = (2.0f * near) / (right - left);
- r.E[0][1] = 0.0f;
- r.E[0][2] = (right + left) / (right - left);
- r.E[0][3] = 0.0f;
-
- r.E[1][0] = 0.0f;
- r.E[1][1] = (2.0f * near) / (top - bottom);
- r.E[1][2] = (top + bottom) / (top - bottom);
- r.E[1][3] = 0.0f;
-
- r.E[2][0] = 0.0f;
- r.E[2][1] = 0.0f;
- r.E[2][2] = -(far + near) / (far - near);
- r.E[2][3] = (-2.0f * far * near) / (far - near);
-
- r.E[3][0] = 0.0f;
- r.E[3][1] = 0.0f;
- r.E[3][2] = -1.0f;
- r.E[3][3] = 0.0f;
-
- return r;
-}
-
-
-m4 glmth_projection_perspective_fov(f32 fovy, f32 aspect, f32 near, f32 far)
-{
- f32 half_height = glmth_tanf(fovy / 2.0f) * near;
- f32 half_width = half_height * aspect;
- f32 left = -half_width;
- f32 right = half_width;
- f32 bottom = -half_height;
- f32 top = half_height;
-
- return glmth_projection_perspective(left, right, bottom, top, near, far);
-}
diff --git a/src/glmth.h b/src/glmth.h
@@ -1,24 +1,19 @@
-#ifndef GLMTH_H
-#define GLMTH_H
-
-#include <assert.h>
-#include <stdbool.h>
-#include <stdint.h>
+#pragma once
#ifndef M_PI
#define M_PI 3.14159265359f
#endif
-typedef uint8_t u8;
-typedef uint16_t u16;
-typedef uint32_t u32;
-typedef uint64_t u64;
-typedef int8_t s8;
-typedef int16_t s16;
-typedef int32_t s32;
-typedef int64_t s64;
-typedef float f32;
-typedef double r64;
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+typedef signed char i8;
+typedef signed short i16;
+typedef signed int i32;
+typedef signed long long i64;
+typedef float f32;
+typedef double f64;
#define GLMTH_PI 3.14159265358979323846f
#define GLMTH_M_2_PI 6.28318530717958647693f
@@ -28,11 +23,10 @@ typedef double r64;
static inline f32 glmth_floorf(f32 x)
{
- f32 result = (f32)((s32)x - (x < 0.0f));
+ f32 result = (f32)((i32)x - (x < 0.0f));
return result;
}
-
// These Sine and Cosine approximations use a parabola adjusted to minimize
// error. This lovely approximation was derived by "Nick" on the devmaster.net
// forums:
@@ -200,37 +194,165 @@ typedef struct
f32 E[4][4]; // E[row][column]
} m4;
+static inline m4 glmth_m4_init_id(void)
+{
+ m4 m = { 0 };
+ m.E[0][0] = 1.0f;
+ m.E[1][1] = 1.0f;
+ m.E[2][2] = 1.0f;
+ m.E[3][3] = 1.0f;
+ return m;
+}
-m4 glmth_m4_init_id();
-void glmth_m4_print(m4 m);
-f32 *glmth_m4_valueptr(m4 m);
-bool glmth_m4m4_eq(m4 mat1, m4 mat2);
-m4 glmth_m4m4_m(m4 mat1, m4 mat2);
-v4 glmth_m4v4_m(m4 m, v4 v);
-v3 glmth_v3_cross(v3 vec1, v3 vec2);
-v3 glmth_v3_init(f32 x, f32 y, f32 z);
-v3 glmth_v3_init_f(f32 f);
-f32 glmth_v3_length(v3 v);
-v3 glmth_v3f_m(v3 v, f32 s);
-v3 glmth_v3_negate(v3 v);
-v3 glmth_v3_normalize(v3 v);
-void glmth_v3_print(v3 v);
-v3 glmth_v3_a(v3 vec1, v3 vec2);
-v3 glmth_v3_s(v3 vec1, v3 vec2);
-void glmth_v4_print(v4 v);
-bool glmth_v4v4_eq(v4 vec1, v4 vec2);
-void glmth_clampf(float *f, float min, float max);
-f32 glmth_deg(f32 rad);
-float glmth_lerpf(float f, float min, float max);
-f32 glmth_rad(f32 deg);
-m4 glmth_rotate_x(m4 m, f32 rad);
-m4 glmth_rotate_y(m4 m, f32 rad);
-m4 glmth_rotate_z(m4 m, f32 rad);
-m4 glmth_rotate(m4 m, f32 rad, v3 axis);
-m4 glmth_scale(m4 m, v3 v);
-m4 glmth_translate(m4 m, v3 v);
-m4 glmth_projection_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far);
-m4 glmth_projection_perspective(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far);
-m4 glmth_projection_perspective_fov(f32 fovy, f32 aspect, f32 near, f32 far);
-m4 glmth_camera_look_at(v3 camera_pos, v3 camera_target, v3 up);
-#endif
+static inline void glmth_m4_valueptr(m4 m, f32* out_valueptr)
+{
+ for (u8 v = 0; v < 16; ++v)
+ {
+ u8 row = v / 4;
+ u8 col = v % 4;
+ out_valueptr[v] = m.E[row][col];
+ }
+}
+
+static inline m4 glmth_m4m4_m(m4 mat1, m4 mat2)
+{
+ m4 r = {
+ .E[0][0] = (mat1.E[0][0] * mat2.E[0][0]) + (mat1.E[0][1] * mat2.E[1][0]) + (mat1.E[0][2] * mat2.E[2][0]) + (mat1.E[0][3] * mat2.E[3][0]),
+ .E[0][1] = (mat1.E[0][0] * mat2.E[0][1]) + (mat1.E[0][1] * mat2.E[1][1]) + (mat1.E[0][2] * mat2.E[2][1]) + (mat1.E[0][3] * mat2.E[3][1]),
+ .E[0][2] = (mat1.E[0][0] * mat2.E[0][2]) + (mat1.E[0][1] * mat2.E[1][2]) + (mat1.E[0][2] * mat2.E[2][2]) + (mat1.E[0][3] * mat2.E[3][2]),
+ .E[0][3] = (mat1.E[0][0] * mat2.E[0][3]) + (mat1.E[0][1] * mat2.E[1][3]) + (mat1.E[0][2] * mat2.E[2][3]) + (mat1.E[0][3] * mat2.E[3][3]),
+
+ .E[1][0] = (mat1.E[1][0] * mat2.E[0][0]) + (mat1.E[1][1] * mat2.E[1][0]) + (mat1.E[1][2] * mat2.E[2][0]) + (mat1.E[1][3] * mat2.E[3][0]),
+ .E[1][1] = (mat1.E[1][0] * mat2.E[0][1]) + (mat1.E[1][1] * mat2.E[1][1]) + (mat1.E[1][2] * mat2.E[2][1]) + (mat1.E[1][3] * mat2.E[3][1]),
+ .E[1][2] = (mat1.E[1][0] * mat2.E[0][2]) + (mat1.E[1][1] * mat2.E[1][2]) + (mat1.E[1][2] * mat2.E[2][2]) + (mat1.E[1][3] * mat2.E[3][2]),
+ .E[1][3] = (mat1.E[1][0] * mat2.E[0][3]) + (mat1.E[1][1] * mat2.E[1][3]) + (mat1.E[1][2] * mat2.E[2][3]) + (mat1.E[1][3] * mat2.E[3][3]),
+
+ .E[2][0] = (mat1.E[2][0] * mat2.E[0][0]) + (mat1.E[2][1] * mat2.E[1][0]) + (mat1.E[2][2] * mat2.E[2][0]) + (mat1.E[2][3] * mat2.E[3][0]),
+ .E[2][1] = (mat1.E[2][0] * mat2.E[0][1]) + (mat1.E[2][1] * mat2.E[1][1]) + (mat1.E[2][2] * mat2.E[2][1]) + (mat1.E[2][3] * mat2.E[3][1]),
+ .E[2][2] = (mat1.E[2][0] * mat2.E[0][2]) + (mat1.E[2][1] * mat2.E[1][2]) + (mat1.E[2][2] * mat2.E[2][2]) + (mat1.E[2][3] * mat2.E[3][2]),
+ .E[2][3] = (mat1.E[2][0] * mat2.E[0][3]) + (mat1.E[2][1] * mat2.E[1][3]) + (mat1.E[2][2] * mat2.E[2][3]) + (mat1.E[2][3] * mat2.E[3][3]),
+
+ .E[3][0] = (mat1.E[3][0] * mat2.E[0][0]) + (mat1.E[3][1] * mat2.E[1][0]) + (mat1.E[3][2] * mat2.E[2][0]) + (mat1.E[3][3] * mat2.E[3][0]),
+ .E[3][1] = (mat1.E[3][0] * mat2.E[0][1]) + (mat1.E[3][1] * mat2.E[1][1]) + (mat1.E[3][2] * mat2.E[2][1]) + (mat1.E[3][3] * mat2.E[3][1]),
+ .E[3][2] = (mat1.E[3][0] * mat2.E[0][2]) + (mat1.E[3][1] * mat2.E[1][2]) + (mat1.E[3][2] * mat2.E[2][2]) + (mat1.E[3][3] * mat2.E[3][2]),
+ .E[3][3] = (mat1.E[3][0] * mat2.E[0][3]) + (mat1.E[3][1] * mat2.E[1][3]) + (mat1.E[3][2] * mat2.E[2][3]) + (mat1.E[3][3] * mat2.E[3][3]),
+ };
+ return r;
+}
+
+static inline v3 glmth_v3_init(f32 x, f32 y, f32 z)
+{
+ v3 v = { .x = x, .y = y, .z = z };
+ return v;
+}
+
+static inline f32 glmth_rad(f32 deg)
+{
+ return deg * (M_PI / 180.0f);
+}
+
+static inline m4 glmth_rotate(m4 m, f32 rad, v3 axis)
+{
+ f32 c = glmth_cosf(rad);
+ f32 s = glmth_sinf(rad);
+
+ m4 r = glmth_m4_init_id();
+
+ r.E[0][0] = c + ((axis.x * axis.x) * (1 - c));
+ r.E[0][1] = (axis.x * axis.y * (1 - c)) - (axis.z * s);
+ r.E[0][2] = (axis.x * axis.z * (1 - c)) + (axis.y * s);
+
+ r.E[1][0] = (axis.y * axis.x * (1 - c)) + (axis.z * s);
+ r.E[1][1] = c + ((axis.y * axis.y) * (1 - c));
+ r.E[1][2] = (axis.y * axis.z * (1 - c)) - (axis.x * s);
+
+ r.E[2][0] = (axis.z * axis.x * (1 - c)) - (axis.y * s);
+ r.E[2][1] = (axis.z * axis.y * (1 - c)) + (axis.x * s);
+ r.E[2][2] = c + ((axis.z * axis.z) * (1 - c));
+
+ return glmth_m4m4_m(m, r);
+}
+
+static inline m4 glmth_translate(m4 m, v3 v)
+{
+ m4 r = glmth_m4_init_id();
+ r.E[0][3] = v.x;
+ r.E[1][3] = v.y;
+ r.E[2][3] = v.z;
+ return glmth_m4m4_m(m, r);
+}
+
+static inline m4 glmth_projection_ortho(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far)
+{
+ // TODO: This assert fails when you minimise the window in Windows.
+ assert(left != right);
+ assert(bottom != top);
+ assert(near != far);
+
+ m4 r = glmth_m4_init_id();
+
+ r.E[0][0] = 2.0f / (right - left);
+ r.E[0][1] = 0.0f;
+ r.E[0][2] = 0.0f;
+ r.E[0][3] = -(right + left) / (right - left);
+
+ r.E[1][0] = 0.0f;
+ r.E[1][1] = 2.0f / (top - bottom);
+ r.E[1][2] = 0.0f;
+ r.E[1][3] = -(top + bottom) / (top - bottom);
+
+ r.E[2][0] = 0.0f;
+ r.E[2][1] = 0.0f;
+ r.E[2][2] = -2.0f / (far - near);
+ r.E[2][3] = -(far + near) / (far - near);
+
+ r.E[3][0] = 0.0f;
+ r.E[3][1] = 0.0f;
+ r.E[3][2] = 0.0f;
+ r.E[3][3] = 1.0f;
+
+ return r;
+}
+
+static inline m4 glmth_projection_perspective(f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far)
+{
+ assert(left != right);
+ assert(bottom != top);
+ assert(near != far);
+
+ m4 r = glmth_m4_init_id();
+
+ r.E[0][0] = (2.0f * near) / (right - left);
+ r.E[0][1] = 0.0f;
+ r.E[0][2] = (right + left) / (right - left);
+ r.E[0][3] = 0.0f;
+
+ r.E[1][0] = 0.0f;
+ r.E[1][1] = (2.0f * near) / (top - bottom);
+ r.E[1][2] = (top + bottom) / (top - bottom);
+ r.E[1][3] = 0.0f;
+
+ r.E[2][0] = 0.0f;
+ r.E[2][1] = 0.0f;
+ r.E[2][2] = -(far + near) / (far - near);
+ r.E[2][3] = (-2.0f * far * near) / (far - near);
+
+ r.E[3][0] = 0.0f;
+ r.E[3][1] = 0.0f;
+ r.E[3][2] = -1.0f;
+ r.E[3][3] = 0.0f;
+
+ return r;
+}
+
+static inline m4 glmth_projection_perspective_fov(f32 fovy, f32 aspect, f32 near, f32 far)
+{
+ f32 half_height = glmth_tanf(fovy / 2.0f) * near;
+ f32 half_width = half_height * aspect;
+ f32 left = -half_width;
+ f32 right = half_width;
+ f32 bottom = -half_height;
+ f32 top = half_height;
+
+ return glmth_projection_perspective(left, right, bottom, top, near, far);
+}
diff --git a/src/platform.h b/src/platform.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#define PLATFORM_READ_ENTIRE_FILE(name) char *(name)(char *file_path)
+typedef PLATFORM_READ_ENTIRE_FILE(platform_read_entire_file_func);
+
+#define PLATFORM_PRINT(name) int (name)(const char *format, ...)
+typedef PLATFORM_PRINT(platform_print_func);
+
+#define PLATFORM_MEMORY_FREE(name) void (name)(void *ptr)
+typedef PLATFORM_MEMORY_FREE(platform_memory_free_func);
+
+struct PlatformApi
+{
+ platform_read_entire_file_func* platform_read_entire_file;
+ platform_print_func* platform_print;
+ platform_memory_free_func* platform_memory_free;
+};
+
+struct GameState
+{
+ u32 cube_vao;
+ u32 cube_vbo;
+ u32 cube_ebo;
+ struct Shader cube_shader;
+ struct PlatformApi platform;
+};
+
+#ifdef PLATFORM_HOTLOAD_GAME_CODE
+#include <time.h>
+// We need to call this from the platform layer in order for the game, when
+// built as a shared object library to have access to the OpenGL symbols.
+// https://github.com/Dav1dde/glad/issues/151
+typedef void (game_load_opengl_symbols_func)(void);
+
+typedef void (game_update_and_render_func)(struct GameState *game_state, float dt, u32 screen_width, u32 screen_height);
+
+struct GameCode
+{
+ bool is_valid;
+ void *game_code_library;
+ time_t last_write_time;
+ game_load_opengl_symbols_func *game_load_opengl_symbols;
+ game_update_and_render_func *game_update_and_render;
+};
+#endif // PLATFORM_HOTLOAD_GAME_CODE
diff --git a/src/platform_emscripten.c b/src/platform_emscripten.c
@@ -1,5 +1,3 @@
-#include "platform_emscripten.h"
-
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
@@ -10,6 +8,8 @@
#define GLFW_INCLUDE_ES3
#include <GLFW/glfw3.h>
+#include "game.c"
+#include "platform_emscripten.h"
void error_callback(int error, const char *description);
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
diff --git a/src/platform_emscripten.h b/src/platform_emscripten.h
@@ -4,9 +4,6 @@
#include <stdbool.h>
#include <time.h>
-#include <GLFW/glfw3.h>
-#include "game.h"
-
#define PLATFORM_SCR_WIDTH 600
#define PLATFORM_SCR_HEIGHT 600
diff --git a/src/platform_linux.c b/src/platform_linux.c
@@ -1,5 +1,3 @@
-#include "platform_linux.h"
-
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
@@ -11,6 +9,9 @@
#include <glad/glad.h>
#include <GLFW/glfw3.h>
+#include "game.c"
+#include "platform.h"
+#include "platform_linux.h"
void error_callback(int error, const char* description);
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
@@ -71,6 +72,10 @@ int main(void)
#endif // USE_TEST_SEED
struct GameState game_state = {0};
+ game_state.platform.platform_read_entire_file = &linux_read_entire_file;
+ game_state.platform.platform_print = &linux_print;
+ game_state.platform.platform_memory_free = &linux_memory_free;
+
game_init(&game_state, PLATFORM_SCR_WIDTH, PLATFORM_SCR_HEIGHT);
#ifdef PLATFORM_HOTLOAD_GAME_CODE
@@ -151,6 +156,56 @@ time_t file_get_modified_time(char *file_path)
return mtime;
}
+PLATFORM_MEMORY_FREE(linux_memory_free)
+{
+ free(ptr);
+}
+
+PLATFORM_PRINT(linux_print)
+{
+ va_list args;
+ va_start(args, format);
+ int num_chars_printed = vprintf(format, args);
+ va_end(args);
+ return num_chars_printed;
+}
+
+PLATFORM_READ_ENTIRE_FILE(linux_read_entire_file)
+{
+ FILE *handle = fopen(file_path, "r");
+ char *buffer = NULL;
+
+ if (handle)
+ {
+ // get file size
+ fseek(handle, 0, SEEK_END);
+ u32 num_bytes_in_file = ftell(handle);
+ rewind(handle);
+
+ // TODO: replace malloc with own allocator so I stop having nightmares
+ buffer = (char*) malloc(sizeof(char) * (num_bytes_in_file + 1) );
+
+ u32 bytes_read = fread(buffer, sizeof(char), num_bytes_in_file, handle);
+ // IMPORTANT! fread() doesn't add the '\0'
+ buffer[num_bytes_in_file] = '\0';
+
+ if (num_bytes_in_file != bytes_read)
+ {
+ free(buffer);
+ buffer = NULL;
+ }
+
+ fclose(handle);
+ }
+ else
+ {
+ printf("Error: Couldn't open file at path: %s", file_path);
+ // TODO: handle errors here in a better way
+ exit(1);
+ }
+
+ return buffer;
+}
#ifdef PLATFORM_HOTLOAD_GAME_CODE
void unload_game_code(struct GameCode *game_code)
diff --git a/src/platform_linux.h b/src/platform_linux.h
@@ -4,8 +4,6 @@
#include <stdbool.h>
#include <time.h>
-#include "game.h"
-
#define PLATFORM_SCR_WIDTH 600
#define PLATFORM_SCR_HEIGHT 600
@@ -15,16 +13,12 @@
#define PLATFORM_UPDATES_PER_SECOND 120
#define PLATFORM_MS_PER_UPDATE (PLATFORM_SECOND / PLATFORM_UPDATES_PER_SECOND)
+PLATFORM_READ_ENTIRE_FILE(linux_read_entire_file);
+PLATFORM_PRINT(linux_print);
+PLATFORM_MEMORY_FREE(linux_memory_free);
+
#ifdef PLATFORM_HOTLOAD_GAME_CODE
-#define PLATFORM_GAME_LIB_PATH "./build/release/game.so"
-struct GameCode
-{
- bool is_valid;
- void *game_code_library;
- time_t last_write_time;
- game_load_opengl_symbols_func *game_load_opengl_symbols;
- game_update_and_render_func *game_update_and_render;
-};
+#define PLATFORM_GAME_LIB_PATH "./out/release/game.so"
#endif // PLATFORM_HOTLOAD_GAME_CODE
#endif // PLATFORM_LINUX_H
diff --git a/src/platform_wasm.c b/src/platform_wasm.c
@@ -0,0 +1,90 @@
+#include "game.c"
+#include "platform.h"
+#include "platform_wasm.h"
+
+#define export __attribute__( ( visibility( "default" ) ) )
+
+char *js_read_entire_file(i32 file_path, i32 name_len, char *file_data);
+int js_print(i32 string, i32 len);
+
+struct GameState g_game_state = {0};
+i32 g_width = PLATFORM_SCR_WIDTH;
+i32 g_height = PLATFORM_SCR_HEIGHT;
+char g_mem_buffer[1000] = {0};
+i32 g_mem_buffer_i = 0;
+u32 time = 0;
+
+
+export bool init(void)
+{
+ g_game_state.platform.platform_read_entire_file = &wasm_read_entire_file;
+ g_game_state.platform.platform_print = &wasm_print;
+ g_game_state.platform.platform_memory_free = &wasm_memory_free;
+ game_init(&g_game_state, g_width, g_height);
+ return true;
+}
+
+export void render(void)
+{
+ time += 16;
+ game_update_and_render(&g_game_state, time/1000.0f, g_width, g_height);
+}
+
+export void window_resize(int w, int h)
+{
+ g_width = w;
+ g_height = h;
+ glViewport(0, 0, g_width, g_height);
+}
+
+i32 str_length(const char *str)
+{
+ i32 i = 0;
+ while(str[i] != '\0')
+ {
+ i++;
+ }
+ return i;
+}
+
+void *memset(void *s, int c, size_t n)
+{
+ u8 *p = s;
+ while(n > 0)
+ {
+ *p = (u8)c;
+ p++;
+ n--;
+ }
+ return s;
+}
+
+PLATFORM_READ_ENTIRE_FILE(wasm_read_entire_file)
+{
+ g_mem_buffer_i++;
+ char *buf = &g_mem_buffer[g_mem_buffer_i];
+ if(js_read_entire_file((i32)file_path, str_length(file_path), buf))
+ {
+ i32 file_data_length = str_length(buf);
+ g_mem_buffer_i += file_data_length;
+ g_mem_buffer[g_mem_buffer_i] = '\0';
+ wasm_print("Succeeded in reading file.");
+ }
+ else
+ {
+ wasm_print("Failed to read file.");
+ }
+ return buf;
+}
+
+PLATFORM_PRINT(wasm_print)
+{
+ i32 len = str_length(format);
+ js_print((i32)format, len);
+ return 0;
+}
+
+PLATFORM_MEMORY_FREE(wasm_memory_free)
+{
+ g_mem_buffer_i = 0;
+}
diff --git a/src/platform_wasm.h b/src/platform_wasm.h
@@ -0,0 +1,6 @@
+#define PLATFORM_SCR_WIDTH 600
+#define PLATFORM_SCR_HEIGHT 600
+
+PLATFORM_READ_ENTIRE_FILE(wasm_read_entire_file);
+PLATFORM_PRINT(wasm_print);
+PLATFORM_MEMORY_FREE(wasm_memory_free);
diff --git a/src/platform_wasm_index.html b/src/platform_wasm_index.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>Transparent Cube</title>
+<meta name=viewport content='width=device-width,initial-scale=1' charset='utf-8'>
+<style>
+body {
+ margin: 0;
+ overflow: hidden;
+}
+#webglcanvas {
+ width: 100vw;
+ height: 100vh;
+}
+</style>
+<canvas id=webglcanvas></canvas>
+<script src='script.js' defer></script>
diff --git a/src/platform_wasm_js_symbols.txt b/src/platform_wasm_js_symbols.txt
@@ -0,0 +1,37 @@
+webglAttachShader
+webglBindBuffer
+webglBindVertexArray
+webglBlendColor
+webglBlendFunc
+webglBufferData
+webglClear
+webglClearColor
+webglCompileShader
+webglCreateBuffer
+webglCreateProgram
+webglCreateShader
+webglCreateVertexArray
+webglDeleteBuffer
+webglDeleteShader
+webglDeleteVertexArray
+webglDepthMask
+webglDisable
+webglDrawElements
+webglEnable
+webglEnableVertexAttribArray
+webglGetProgramInfoLog
+webglGetProgramParameter
+webglGetShaderInfoLog
+webglGetShaderParameter
+webglGetUniformLocation
+webglLinkProgram
+webglShaderSource
+webglUniform1f
+webglUniform1i
+webglUniform3f
+webglUniformMatrix4fv
+webglUseProgram
+webglVertexAttribPointer
+webglViewport
+js_read_entire_file
+js_print
diff --git a/src/platform_wasm_loader.js b/src/platform_wasm_loader.js
@@ -0,0 +1,248 @@
+let utf8decoder = new TextDecoder("utf-8");
+let memory = null;
+let exports = {};
+let imports = {};
+let files = [];
+let gl = null;
+let gl_id_freelist = [];
+let gl_id_map = [null];
+imports["webglAttachShader"] = function(program_id, shader_id) {
+ let program = gl_id_map[program_id];
+ let shader = gl_id_map[shader_id];
+ gl.attachShader(program, shader);
+}
+imports["webglBindBuffer"] = function(target, buffer_id) {
+ let buffer = gl_id_map[buffer_id];
+ gl.bindBuffer(target, buffer);
+}
+imports["webglBindVertexArray"] = function(vao_id) {
+ let vao = gl_id_map[vao_id];
+ gl.bindVertexArray(vao);
+}
+imports["webglBlendColor"] = function(red, green, blue, alpha) {
+ gl.blendColor(red, green, blue, alpha);
+}
+imports["webglBlendFunc"] = function(sfactor, dfactor) {
+ gl.blendFunc(sfactor, dfactor);
+}
+imports["webglBufferData"] = function(target, size, data, usage) {
+ let dataslice = memory.subarray(data, data + size);
+ gl.bufferData(target, dataslice, usage);
+}
+imports["webglClear"] = function(mask) {
+ gl.clear(mask);
+}
+imports["webglClearColor"] = function(red, green, blue, alpha) {
+ gl.clearColor(red, green, blue, alpha);
+}
+imports["webglCompileShader"] = function(shader_id) {
+ let shader = gl_id_map[shader_id];
+ gl.compileShader(shader);
+}
+imports["webglCreateBuffer"] = function() {
+ let buffer = gl.createBuffer();
+ let buffer_id = webgl_id_new(buffer);
+ return buffer_id
+}
+imports["webglCreateProgram"] = function() {
+ let program = gl.createProgram();
+ let program_id = webgl_id_new(program);
+ return program_id;
+}
+imports["webglCreateShader"] = function(type) {
+ let shader = gl.createShader(type);
+ let shader_id = webgl_id_new(shader);
+ return shader_id;
+}
+imports["webglCreateVertexArray"] = function() {
+ let vao = gl.createVertexArray()
+ let vao_id = webgl_id_new(vao);
+ return vao_id
+}
+imports["webglDeleteBuffer"] = function(buffer_id) {
+ let buffer = gl_id_map[buffer_id];
+ gl.deleteBuffer(buffer);
+ webgl_id_remove(buffer_id);
+}
+imports["webglDeleteShader"] = function(shader_id) {
+ let shader = gl_id_map[shader_id];
+ gl.deleteShader(shader);
+ webgl_id_remove(shader_id);
+}
+imports["webglDeleteVertexArray"] = function(vao_id) {
+ let vao = gl_id_map[vao_id];
+ gl.deleteVertexArray(vao);
+ webgl_id_remove(vao_id);
+}
+imports["webglDepthMask"] = function(flag) {
+ gl.depthMask(flag);
+}
+imports["webglDisable"] = function(cap) {
+ gl.disable(cap);
+}
+imports["webglDrawElements"] = function(mode, count, type, offset) {
+ gl.drawElements(mode, count, type, offset);
+}
+imports["webglEnable"] = function(cap) {
+ gl.enable(cap);
+}
+imports["webglEnableVertexAttribArray"] = function(index) {
+ gl.enableVertexAttribArray(index);
+}
+imports["webglGetProgramInfoLog"] = function() {
+}
+imports["webglGetProgramParameter"] = function(program_id, param) {
+ let program = gl_id_map[program_id];
+ return gl.getProgramParameter(program, param);
+}
+imports["webglGetShaderInfoLog"] = function(shader_id, out_buf) {
+ let shader = gl_id_map[shader_id];
+ let info_log = gl.getShaderInfoLog(shader);
+
+ // TODO: remove this once we get sprintf workingk
+ console.log(info_log);
+
+ let arr = memory.subarray(info_log, out_buf + info_log.byteLength);
+ arr.set(new Uint8Array(info_log));
+ return true;
+}
+imports["webglGetShaderParameter"] = function(shader_id, param) {
+ let shader = gl_id_map[shader_id];
+ let result = gl.getShaderParameter(shader, param);
+ return result;
+}
+imports["webglGetUniformLocation"] = function(program_id, name_ptr, name_len) {
+ let program = gl_id_map[program_id];
+ let name = utf8decoder.decode(memory.subarray(name_ptr, name_ptr+name_len));
+ let loc = gl.getUniformLocation(program, name);
+ let location_id = webgl_id_new(loc);
+ return location_id;
+}
+imports["webglLinkProgram"] = function(program_id) {
+ let program = gl_id_map[program_id];
+ gl.linkProgram(program);
+}
+imports["webglShaderSource"] = function(shader_id, source_ptr, source_len) {
+ let shader = gl_id_map[shader_id];
+ let arr = memory.subarray(source_ptr, source_ptr + source_len);
+ let s = utf8decoder.decode(arr);
+ gl.shaderSource(shader, s);
+}
+imports["webglUniform1f"] = function(location_id, value) {
+ let loc = gl_id_map[location_id];
+ gl['uniform1f'](loc, value);
+}
+imports["webglUniform1i"] = function(location_id, value) {
+ let loc = gl_id_map[location_id];
+ gl['uniform1i'](loc, value);
+}
+imports["webglUniform3f"] = function(location_id, x, y, z) {
+ let loc = gl_id_map[location_id];
+ gl['uniform3fv'](loc, [x, y, z]);
+}
+imports["webglUniformMatrix4fv"] = function(location_id, transpose, data) {
+ let loc = gl_id_map[location_id];
+ let dataslice = memory.slice(data, data + 4 * 16);
+ gl.uniformMatrix4fv(loc, transpose, new Float32Array(dataslice.buffer));
+}
+imports["webglUseProgram"] = function(program_id) {
+ let program = gl_id_map[program_id];
+ gl.useProgram(program);
+}
+imports["webglVertexAttribPointer"] = function(index, size, type, normalized, stride, offset) {
+ gl.vertexAttribPointer(index, size, type, normalized, stride, offset);
+}
+imports["webglViewport"] = function(x, y, width, height) {
+ gl.viewport(x, y, width, height);
+}
+imports["js_read_entire_file"] = function(name, name_len, out_buf) {
+ let file_name = utf8decoder.decode(memory.subarray(name, name + name_len))
+ if (file_name == "shader/cube_f.glsl") {
+ var file = files[1];
+ } else if (file_name == "shader/cube_v.glsl") {
+ var file = files[2];
+ } else {
+ return false;
+ }
+ let arr = memory.subarray(out_buf, out_buf + file.byteLength);
+ let s = String.fromCharCode.apply(null, arr);
+ arr.set(new Uint8Array(file));
+ return true;
+}
+imports["js_print"] = function(s, len) {
+ let arr = memory.subarray(s, s + len);
+ console.log(utf8decoder.decode(arr));
+}
+function error_fatal(message) {
+ console.log(message);
+ throw message;
+}
+function webgl_id_new(obj) {
+ if(gl_id_freelist.length == 0) {
+ gl_id_map.push(obj);
+ return gl_id_map.length - 1;
+ } else {
+ let id = gl_id_freelist.shift();
+ gl_id_map[id] = obj;
+ return id;
+ }
+}
+function webgl_id_remove(id) {
+ delete gl_id_map[id];
+ gl_id_freelist.push(id);
+}
+function canvas_resize() {
+ let pr = window.devicePixelRatio;
+ let w = window.innerWidth;
+ let h = window.innerHeight;
+ // Bitwise OR does float truncation
+ let w_pixels = (w * pr) | 0;
+ let h_pixels = (h * pr) | 0;
+ gl.canvas.width = w_pixels;
+ gl.canvas.height = h_pixels
+ exports['window_resize'](w_pixels, h_pixels);
+ console.log("resize: (" + w_pixels + ", " + h_pixels + ")");
+}
+function canvas_render() {
+ exports['render']();
+ window.requestAnimationFrame(canvas_render);
+}
+function file_load(name) {
+ let promise = new Promise((resolve, reject) => {
+ fetch(name).then(resp => {
+ resp.arrayBuffer().then(arr => resolve(arr));
+ });
+ });
+ return promise;
+}
+window.onload = async function() {
+ let ctxopts = {
+ alpha: false,
+ depth: true,
+ stencil: false,
+ antialias: true,
+ preserveDrawingBuffer: false
+ };
+ gl = document.getElementById("webglcanvas").getContext("webgl2", ctxopts);
+ if(!gl) {
+ error_fatal("Your browser does not support WebGL 2.");
+ }
+ files[0] = file_load("binary.wasm");
+ files[1] = file_load("shader/cube_f.glsl");
+ files[2] = file_load("shader/cube_v.glsl");
+ for(var i=0; i<files.length; i++) {
+ files[i] = await files[i];
+ }
+ let binary = files[0];
+ imports['memory'] = new WebAssembly.Memory({'initial':32});
+ memory = new Uint8Array(imports['memory']['buffer']);
+ let program = await WebAssembly.instantiate(binary, {"env":imports});
+ let instance = program['instance'];
+ exports = instance['exports'];
+ canvas_resize();
+ window.addEventListener("resize", canvas_resize);
+ if(!exports['init']()) {
+ error_fatal("Game initialization failed.");
+ }
+ canvas_render();
+}
diff --git a/src/shader.c b/src/shader.c
@@ -1,193 +1,141 @@
-#include "shader.h"
-
-#ifdef __EMSCRIPTEN__
-#include <GLES3/gl3.h>
-#else
-#include "glad/glad.h"
-#endif
-
-
-char *read_file(char *file_path)
+bool shader_compile(const GLchar *vertex_shader_source, const GLchar *fragment_shader_source, struct Shader *compiled_shader)
{
- FILE *handle = fopen(file_path, "r");
- char *buffer = NULL;
+ GLint success;
+ GLchar info_log[512];
- if (handle)
+ if (!(vertex_shader_source && fragment_shader_source))
{
- // get file size
- fseek(handle, 0, SEEK_END);
- uint32_t num_bytes_in_file = ftell(handle);
- rewind(handle);
-
- buffer = (char*) malloc(sizeof(char) * (num_bytes_in_file + 1) );
-
- uint32_t bytes_read = fread(buffer, sizeof(char), num_bytes_in_file, handle);
- // IMPORTANT! fread() doesn't add the '\0'
- buffer[num_bytes_in_file] = '\0';
-
- if (num_bytes_in_file != bytes_read)
- {
- free(buffer);
- buffer = NULL;
- }
-
- fclose(handle);
+ print("Error: One or more shader source files weren't loaded.\n");
+ return false;
}
else
{
- printf("Error: Couldn't open file at path: %s", file_path);
- }
-
- return buffer;
-}
+ GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
+ glCompileShader(vertex_shader);
+ glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
+ if (!success)
+ {
+ glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
+ print("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n %s\n", info_log);
+ // TODO: handle errors here in a better way
+ return false;
+ }
+ GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
+ glCompileShader(fragment_shader);
+ glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
+ if (!success)
+ {
+ glGetShaderInfoLog(fragment_shader, 512, NULL, info_log);
+ print("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n %s\n", info_log);
+ // TODO: handle errors here in a better way
+ return false;
+ }
-struct Shader shader_compile(char *vertex_path, char *fragment_path)
-{
- const GLchar *vertex_shader_source = read_file(vertex_path);
- const GLchar *fragment_shader_source = read_file(fragment_path);
+ compiled_shader->program = glCreateProgram();
+ glAttachShader(compiled_shader->program, vertex_shader);
+ glAttachShader(compiled_shader->program, fragment_shader);
+ glLinkProgram(compiled_shader->program);
+ glGetProgramiv(compiled_shader->program, GL_LINK_STATUS, &success);
+ if (!success)
+ {
+ glGetShaderInfoLog(compiled_shader->program, 512, NULL, info_log);
+ print("ERROR::SHADER::LINKING_FAILED\n %s\n", info_log);
+ // TODO: handle errors here in a better way
+ return false;
+ }
- GLint success;
- GLchar info_log[512];
+ glDeleteShader(fragment_shader);
+ glDeleteShader(vertex_shader);
- GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
- glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
- glCompileShader(vertex_shader);
- glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
- if (!success)
- {
- glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
- printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n %s\n", info_log);
+ return true;
}
+}
- GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
- glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
- glCompileShader(fragment_shader);
- glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
- if (!success)
+bool shader_check_pointer(struct Shader *s)
+{
+ if (s)
{
- glGetShaderInfoLog(fragment_shader, 512, NULL, info_log);
- printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n %s\n", info_log);
+ return true;
}
-
- struct Shader s;
- s.program = glCreateProgram();
- glAttachShader(s.program, vertex_shader);
- glAttachShader(s.program, fragment_shader);
- glLinkProgram(s.program);
- glGetProgramiv(s.program, GL_LINK_STATUS, &success);
- if (!success)
+ else
{
- glGetShaderInfoLog(s.program, 512, NULL, info_log);
- printf("ERROR::SHADER::LINKING_FAILED\n %s\n", info_log);
+ print("Error: invalid Shader pointer\n");
+ return false;
}
-
- glDeleteShader(fragment_shader);
- glDeleteShader(vertex_shader);
-
- return s;
}
-
void shader_use(struct Shader *s)
{
- if (s)
+ if (shader_check_pointer(s))
{
glUseProgram(s->program);
}
- else
- {
- printf("Error: invalid Shader pointer\n");
- }
}
void shader_setb(struct Shader *s, char *name, bool value)
{
- if (s)
+ if (shader_check_pointer(s))
{
glUniform1i(glGetUniformLocation(s->program, name), (int)value);
}
- else
- {
- printf("Error: invalid Shader pointer\n");
- }
}
void shader_seti(struct Shader *s, char *name, int value)
{
- if (s)
+ if (shader_check_pointer(s))
{
glUniform1i(glGetUniformLocation(s->program, name), value);
}
- else
- {
- printf("Error: invalid Shader pointer\n");
- }
}
void shader_setf(struct Shader *s, char *name, f32 value)
{
- if (s)
+ if (shader_check_pointer(s))
{
glUniform1f(glGetUniformLocation(s->program, name), value);
}
- else
- {
- printf("Error: invalid Shader pointer\n");
- }
}
void shader_setm4(struct Shader *s, char *name, m4 *mat)
{
- if (s)
- {
- glUniformMatrix4fv(glGetUniformLocation(s->program, name), 1, GL_TRUE, glmth_m4_valueptr(*mat));
- }
- else
+ if (shader_check_pointer(s))
{
- printf("Error: invalid Shader pointer\n");
+ f32 valueptr[sizeof(m4)];
+ glmth_m4_valueptr(*mat, valueptr);
+ glUniformMatrix4fv(glGetUniformLocation(s->program, name), 1, GL_TRUE, valueptr);
}
}
void shader_setf3(struct Shader *s, char *name, f32 x, f32 y, f32 z)
{
- if (s)
+ if (shader_check_pointer(s))
{
glUniform3f(glGetUniformLocation(s->program, name), x, y, z);
}
- else
- {
- printf("Error: invalid Shader pointer\n");
- }
}
void shader_setf3_1(struct Shader *s, char *name, f32 f)
{
- if (s)
+ if (shader_check_pointer(s))
{
shader_setf3(s, name, f, f, f);
}
- else
- {
- printf("Error: invalid Shader pointer\n");
- }
}
void shader_setv3(struct Shader *s, char *name, v3 *v)
{
- if (s)
+ if (shader_check_pointer(s))
{
shader_setf3(s, name, v->x, v->y, v->z);
}
- else
- {
- printf("Error: invalid Shader pointer\n");
- }
}
diff --git a/src/shader.h b/src/shader.h
@@ -1,19 +1,9 @@
-#ifndef SHADER_H
-#define SHADER_H
-
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "glmth.h"
-
struct Shader
{
- uint32_t program;
+ u32 program;
};
-char *read_file(char *file_path);
-struct Shader shader_compile(char *vertex_path, char *fragment_path);
+bool shader_compile(const GLchar *vertex_shader_source, const GLchar *fragment_shader_source, struct Shader *compiled_shader);
void shader_use(struct Shader *s);
void shader_setb(struct Shader *s, char *name, bool value);
void shader_seti(struct Shader *s, char *name, int value);
@@ -22,5 +12,3 @@ void shader_setm4(struct Shader *s, char *name, m4 *mat);
void shader_setf3(struct Shader *s, char *name, f32 x, f32 y, f32 z);
void shader_setf3_1(struct Shader *s, char *name, f32 f);
void shader_setv3(struct Shader *s, char *name, v3 *v);
-
-#endif
diff --git a/src/webgl.h b/src/webgl.h
@@ -0,0 +1,278 @@
+#pragma once
+
+typedef int i32;
+typedef float f32;
+typedef double f64;
+
+// NOTE(amin): Since these functions will be implemented in javascript, we can
+// only use i32, f32, and f64 params.
+void webglAttachShader(i32 program, i32 shader);
+void webglBindBuffer(i32 target, i32 buffer);
+void webglBindVertexArray(i32 vao);
+void webglBlendColor(f32 r, f32 g, f32 b, f32 a);
+void webglBlendFunc(i32 sfactor, i32 dfactor);
+void webglBufferData(i32 target, i32 size, i32 data, i32 usage);
+void webglClear(i32 mask);
+void webglClearColor(f32 r, f32 g, f32 b, f32 a);
+void webglCompileShader(i32 shader);
+i32 webglCreateBuffer(void);
+i32 webglCreateProgram(void);
+i32 webglCreateShader(i32 type);
+i32 webglCreateVertexArray(void);
+void webglDeleteBuffer(i32 bo);
+void webglDeleteShader(i32 shader);
+void webglDeleteVertexArray(i32 vao);
+void webglDepthMask(i32 flag);
+void webglDisable(i32 cap);
+void webglDrawElements(i32 mode, i32 count, i32 type, i32 offset);
+void webglEnable(i32 cap);
+void webglEnableVertexAttribArray(i32 index);
+void webglGetProgramInfoLog(void);
+int webglGetProgramParameter(i32 program, i32 param);
+void webglGetShaderInfoLog(i32 shader, char *out_buf);
+int webglGetShaderParameter(i32 shader, i32 param);
+i32 webglGetUniformLocation(i32 program, const char name[static 1], i32 name_len);
+void webglLinkProgram(i32 program);
+void webglShaderSource(i32 shader, const char source[static 1], i32 source_len);
+void webglUniform1f(i32 location, f32 value);
+void webglUniform1i(i32 location, i32 value);
+void webglUniform3f(i32 location, f32 x, f32 y, f32 z);
+void webglUniformMatrix4fv(i32 location, i32 transpose, const f32 data[static 16]);
+void webglUseProgram(i32 program);
+void webglVertexAttribPointer(i32 index, i32 size, i32 type, i32 normalized, i32 stride, i32 offset);
+void webglViewport(i32 x, i32 y, i32 width, i32 height);
+
+#define GL_DEPTH_BUFFER_BIT 0x00000100
+#define GL_COLOR_BUFFER_BIT 0x00004000
+#define GL_FALSE 0
+#define GL_TRUE 1
+#define GL_TRIANGLES 0x0004
+#define GL_SRC_ALPHA 0x0302
+#define GL_DEPTH_TEST 0x0B71
+#define GL_BLEND 0x0BE2
+#define GL_UNSIGNED_INT 0x1405
+#define GL_FLOAT 0x1406
+#define GL_CONSTANT_ALPHA 0x8003
+#define GL_ARRAY_BUFFER 0x8892
+#define GL_ELEMENT_ARRAY_BUFFER 0x8893
+#define GL_STATIC_DRAW 0x88E4
+#define GL_FRAGMENT_SHADER 0x8B30
+#define GL_VERTEX_SHADER 0x8B31
+#define GL_COMPILE_STATUS 0x8B81
+#define GL_LINK_STATUS 0x8B82
+
+typedef unsigned int GLenum;
+typedef unsigned char GLboolean;
+typedef unsigned int GLbitfield;
+typedef void GLvoid;
+typedef int GLint;
+typedef unsigned int GLuint;
+typedef int GLsizei;
+typedef float GLfloat;
+typedef char GLchar;
+typedef long GLsizeiptr;
+
+// TODO: add a version with safety checks
+#define WEBGL_CAST_I32(x) (i32)(x)
+
+static inline i32 _webgl_strlen(const char *str)
+{
+ i32 len = 0;
+ while(str[len] != '\0')
+ {
+ len++;
+ }
+ return len;
+}
+
+inline void glAttachShader(GLuint program, GLuint shader)
+{
+ webglAttachShader(WEBGL_CAST_I32(program), WEBGL_CAST_I32(shader));
+}
+
+inline void glBindBuffer(GLenum target, GLuint buffer)
+{
+ webglBindBuffer(WEBGL_CAST_I32(target), WEBGL_CAST_I32(buffer));
+}
+
+inline void glBindVertexArray(GLuint array)
+{
+ webglBindVertexArray(WEBGL_CAST_I32(array));
+}
+
+inline void glBlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+{
+ webglBlendColor((f32)red, (f32)green, (f32)blue, (f32)alpha);
+}
+
+inline void glBlendFunc(GLenum sfactor, GLenum dfactor)
+{
+ webglBlendFunc(WEBGL_CAST_I32(sfactor), WEBGL_CAST_I32(dfactor));
+}
+
+inline void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage)
+{
+ webglBufferData(WEBGL_CAST_I32(target), WEBGL_CAST_I32(size), WEBGL_CAST_I32(data), WEBGL_CAST_I32(usage));
+}
+
+inline void glClear(GLbitfield mask)
+{
+ webglClear(WEBGL_CAST_I32(mask));
+}
+
+inline void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
+{
+ webglClearColor((f32)red, (f32)green, (f32)blue, (f32)alpha);
+}
+
+inline void glCompileShader(GLuint shader)
+{
+ webglCompileShader(WEBGL_CAST_I32(shader));
+}
+
+inline GLuint glCreateProgram(void)
+{
+ i32 program_id = webglCreateProgram();
+ return (GLuint)program_id;
+}
+
+inline GLuint glCreateShader(GLenum type)
+{
+ return webglCreateShader(WEBGL_CAST_I32(type));
+}
+
+inline void glDeleteBuffers(GLsizei n, const GLuint *buffers)
+{
+ assert(n == 1);
+ i32 the_buffer = WEBGL_CAST_I32(buffers[0]);
+ webglDeleteBuffer(the_buffer);
+}
+
+inline void glDeleteShader(GLuint shader)
+{
+ webglDeleteShader(WEBGL_CAST_I32(shader));
+}
+
+inline void glDeleteVertexArrays(GLsizei n, const GLuint *arrays)
+{
+ assert(n == 1);
+ i32 the_array = WEBGL_CAST_I32(arrays[0]);
+ webglDeleteVertexArray(the_array);
+}
+
+inline void glDepthMask(GLboolean flag)
+{
+ webglDepthMask(WEBGL_CAST_I32(flag));
+}
+
+inline void glDisable(GLenum cap)
+{
+ webglDisable(WEBGL_CAST_I32(cap));
+}
+
+inline void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices)
+{
+ webglDrawElements(WEBGL_CAST_I32(mode), WEBGL_CAST_I32(count), WEBGL_CAST_I32(type), WEBGL_CAST_I32(indices));
+}
+
+inline void glEnable(GLenum cap)
+{
+ webglEnable(WEBGL_CAST_I32(cap));
+}
+
+inline void glEnableVertexAttribArray(GLuint index)
+{
+ webglEnableVertexAttribArray(WEBGL_CAST_I32(index));
+}
+
+inline void glGenBuffers(GLsizei n, GLuint *buffers)
+{
+ assert(n == 1);
+ i32 buffer_id = webglCreateBuffer();
+ assert(buffer_id >= 0);
+ *buffers = (GLuint)buffer_id;
+}
+
+inline void glGenVertexArrays(GLsizei n, GLuint *arrays)
+{
+ assert(n == 1);
+ i32 vao_id = webglCreateVertexArray();
+ assert(vao_id >= 0);
+ *arrays = (GLuint)vao_id;
+}
+
+inline void glGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog)
+{
+ // TODO: implement
+ //webglGetProgramInfoLog(WEBGL_CAST_I32(program), WEBGL_CAST_I32(bufsize), WEBGL_CAST_I32(*length), WEBGL_CAST_I32(*infoLog));
+}
+
+inline void glGetProgramiv(GLuint program, GLenum pname, GLint *params)
+{
+ *params = webglGetProgramParameter(WEBGL_CAST_I32(program), WEBGL_CAST_I32(pname));
+}
+
+inline void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog)
+{
+ webglGetShaderInfoLog(WEBGL_CAST_I32(shader), infoLog);
+}
+
+inline void glGetShaderiv(GLuint shader, GLenum pname, GLint *params)
+{
+ *params = webglGetShaderParameter(WEBGL_CAST_I32(shader), WEBGL_CAST_I32(pname));
+}
+
+inline GLint glGetUniformLocation(GLuint program, const GLchar *name)
+{
+ i32 name_len = _webgl_strlen(name);
+ return webglGetUniformLocation(WEBGL_CAST_I32(program), name, name_len);
+}
+
+inline void glLinkProgram(GLuint program)
+{
+ webglLinkProgram(WEBGL_CAST_I32(program));
+}
+
+inline void glShaderSource(GLuint shader, GLsizei count, const GLchar *const *string, const GLint *length)
+{
+ const GLchar *s = (void *)*string;
+ i32 l = _webgl_strlen(s);
+ webglShaderSource(WEBGL_CAST_I32(shader), s, l);
+}
+
+inline void glUniform1f(GLint location, GLfloat v0)
+{
+ webglUniform1f(WEBGL_CAST_I32(location), (f32)v0);
+}
+
+inline void glUniform1i(GLint location, GLint v0)
+{
+ webglUniform1i(WEBGL_CAST_I32(location), WEBGL_CAST_I32(v0));
+}
+
+inline void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2)
+{
+ webglUniform3f(WEBGL_CAST_I32(location), (f32)v0, (f32)v1, (f32)v2);
+}
+
+inline void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
+{
+ webglUniformMatrix4fv(WEBGL_CAST_I32(location), WEBGL_CAST_I32(transpose), value);
+}
+
+inline void glUseProgram(GLuint program)
+{
+ webglUseProgram(WEBGL_CAST_I32(program));
+}
+
+inline void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer)
+{
+ webglVertexAttribPointer(WEBGL_CAST_I32(index), WEBGL_CAST_I32(size), WEBGL_CAST_I32(type), WEBGL_CAST_I32(normalized), WEBGL_CAST_I32(stride), WEBGL_CAST_I32(pointer));
+}
+
+inline void glViewport(GLint x, GLint y, GLsizei width, GLsizei height)
+{
+ webglViewport(x, y, WEBGL_CAST_I32(width), WEBGL_CAST_I32(height));
+}
+
+#undef WEBGL_CAST_I32