transparent-cube

Minimal cross-platform native/wasm graphics example.
git clone git://git.amin.space/transparent-cube.git
Log | Files | Refs | README | LICENSE

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:
M.gitignore | 5++++-
MMakefile | 14+++++++-------
Abuild_wasm.sh | 40++++++++++++++++++++++++++++++++++++++++
Dshader/pyramid_f.glsl | 12------------
Dshader/pyramid_v.glsl | 12------------
Msrc/game.c | 124+++++++++++++++++++++++--------------------------------------------------------
Msrc/game.h | 65++++++++++++++++++++++++++++++++++-------------------------------
Dsrc/glmth.c | 373-------------------------------------------------------------------------------
Msrc/glmth.h | 224+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Asrc/platform.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Msrc/platform_emscripten.c | 4++--
Msrc/platform_emscripten.h | 3---
Msrc/platform_linux.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/platform_linux.h | 16+++++-----------
Asrc/platform_wasm.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/platform_wasm.h | 6++++++
Asrc/platform_wasm_index.html | 15+++++++++++++++
Asrc/platform_wasm_js_symbols.txt | 37+++++++++++++++++++++++++++++++++++++
Asrc/platform_wasm_loader.js | 248+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/shader.c | 176++++++++++++++++++++++++++++---------------------------------------------------
Msrc/shader.h | 16++--------------
Asrc/webgl.h | 278+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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