transparent-cube

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

commit 62426a5e951917871d7280f60c87360f24e93b22
parent 0f78c5c0fc6764dbd0e5760c510273c2a145797b
Author: amin <dev@aminmesbah.com>
Date:   Sat, 15 Jun 2019 22:19:46 +0000

Add header translating gl calls to webgl calls

The webgl* functions will be implemented in javascript. This technique
is based on this excellent article:

https://aransentin.github.io/cwasm/

FossilOrigin-Name: 16d708b7c1ba8101936cf0087816377cc5aecb4dc5280bd3643d173d426c2b5b
Diffstat:
M.gitignore | 3+++
Abuild_wasm.sh | 7+++++++
Msrc/game.c | 30++++++++++++++----------------
Msrc/game.h | 27+++++++++++++--------------
Msrc/glmth.h | 20++++++++++----------
Msrc/shader.c | 12++++++------
Msrc/shader.h | 2+-
Asrc/webgl.h | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Awasm_js_implemented_symbols.txt | 34++++++++++++++++++++++++++++++++++
9 files changed, 352 insertions(+), 47 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,6 +1,9 @@ *.out *.swp +*.bc +*.o + build/* tags diff --git a/build_wasm.sh b/build_wasm.sh @@ -0,0 +1,7 @@ +mkdir -p ./out/wasm +clang -cc1 -Ofast -emit-llvm-bc -triple=wasm32-unknown-unknown-unknown-wasm -std=c11 \ + -DGAME_WEBGL \ + src/game.c +llvm-link -o wasm.bc src/*.bc +llc -O3 -filetype=obj wasm.bc -o wasm.o +wasm-ld --no-entry wasm.o -o binary.wasm --strip-all -allow-undefined-file wasm_js_implemented_symbols.txt --import-memory diff --git a/src/game.c b/src/game.c @@ -1,24 +1,16 @@ #include "game.h" -// TODO: remove references to emscripten -#ifndef __EMSCRIPTEN__ +#ifndef GAME_WEBGL #include "glad.c" #endif #include "shader.c" -#ifdef PLATFORM_HOTLOAD_GAME_CODE -void game_load_opengl_symbols(void) -{ - gladLoadGL(); -} -#endif - void game_init(struct GameState *game_state, uint32_t screen_width, uint32_t 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, @@ -30,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, @@ -39,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); @@ -55,7 +47,7 @@ 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); @@ -71,8 +63,7 @@ void game_init(struct GameState *game_state, uint32_t screen_width, uint32_t scr glEnable(GL_DEPTH_TEST); - // TODO: remove references to emscripten -#ifndef __EMSCRIPTEN__ +#ifndef GAME_WEBGL glEnable(GL_MULTISAMPLE); #endif } @@ -130,3 +121,10 @@ void game_cleanup(struct GameState *game_state) 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 @@ -7,25 +7,30 @@ #include <stdio.h> #include <stdlib.h> -// TODO: remove references to emscripten -#ifdef __EMSCRIPTEN__ -#include <GLES3/gl3.h> +#include "glmth.h" + +#ifdef GAME_WEBGL +#include "webgl.h" #else #include "glad/glad.h" #endif -#include "glmth.h" #include "shader.h" - struct GameState { - GLuint cube_vao; - GLuint cube_vbo; - GLuint cube_ebo; + u32 cube_vao; + u32 cube_vbo; + u32 cube_ebo; struct Shader cube_shader; }; +typedef void (game_update_and_render_func)(struct GameState *game_state, float dt, u32 screen_width, u32 screen_height); +void game_update_and_render(struct GameState *game_state, float dt, u32 screen_width, u32 screen_height); + +void game_init(struct GameState *game_state, u32 screen_width, u32 screen_height); +void game_cleanup(struct GameState *game_state); + #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. @@ -33,9 +38,3 @@ struct GameState 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); diff --git a/src/glmth.h b/src/glmth.h @@ -4,16 +4,16 @@ #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; typedef union { diff --git a/src/shader.c b/src/shader.c @@ -9,12 +9,12 @@ char *read_file(char *file_path) { // get file size fseek(handle, 0, SEEK_END); - uint32_t num_bytes_in_file = ftell(handle); + u32 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); + 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'; @@ -40,10 +40,10 @@ 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); - GLint success; - GLchar info_log[512]; + i32 success; + char info_log[512]; - GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); + u32 vertex_shader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL); glCompileShader(vertex_shader); glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); @@ -53,7 +53,7 @@ struct Shader shader_compile(char *vertex_path, char *fragment_path) printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n %s\n", info_log); } - GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + u32 fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL); glCompileShader(fragment_shader); glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); diff --git a/src/shader.h b/src/shader.h @@ -2,7 +2,7 @@ struct Shader { - uint32_t program; + u32 program; }; char *read_file(char *file_path); diff --git a/src/webgl.h b/src/webgl.h @@ -0,0 +1,264 @@ +#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 shader, i32 program); +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 webglCreateProgram(void); +i32 webglCreateShader(i32 type); +void webglDeleteBuffers(i32 bo); +void webglDeleteShader(i32 shader); +void webglDeleteVertexArrays(i32 vao); +void webglDepthMask(i32 b); +void webglDisable(i32 cap); +void webglDrawElements(i32 mode, i32 count, i32 type, i32 offset); +void webglEnable(i32 cap); +void webglEnableVertexAttribArray(i32 index); +void webglGenBuffers(i32 n, i32 buffers); +void webglGenVertexArrays(i32 n, i32 arrays); +void webglGetProgramInfoLog(void); +int webglGetProgramiv(i32 program, i32 param); +void webglGetShaderInfoLog(void); +int webglGetShaderiv(i32 shader, i32 param); +i32 webglGetUniformLocation(i32 program, const char name[static 1], i32 name_len); +void webglLinkProgram(i32 program); +void webglShaderSource(i32 shader, i32 count, 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, const f32 data[static 16]); +void webglUseProgram(i32 program); +void webglVertexAttribPointer(i32 index, i32 size, i32 type, i32 normalized, i32 stride, i32 offset); + +#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(WEBGL_CAST_I32(red), WEBGL_CAST_I32(green), WEBGL_CAST_I32(blue), WEBGL_CAST_I32(alpha)); +} + +inline void glCompileShader(GLuint shader) +{ + webglCompileShader(WEBGL_CAST_I32(shader)); +} + +inline GLuint glCreateProgram(void) +{ + return webglCreateProgram(); +} + +inline GLuint glCreateShader(GLenum type) +{ + return webglCreateShader(WEBGL_CAST_I32(type)); +} + +inline void glDeleteBuffers(GLsizei n, const GLuint *buffers) +{ + // TODO: assert n == 1 + i32 the_buffer = WEBGL_CAST_I32(buffers[0]); + webglDeleteBuffers(the_buffer); +} + +inline void glDeleteShader(GLuint shader) +{ + webglDeleteShader(WEBGL_CAST_I32(shader)); +} + +inline void glDeleteVertexArrays(GLsizei n, const GLuint *arrays) +{ + // TODO: assert n == 1 + i32 the_array = WEBGL_CAST_I32(arrays[0]); + webglDeleteVertexArrays(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) +{ + webglGenBuffers(WEBGL_CAST_I32(n), WEBGL_CAST_I32(*buffers)); +} + +inline void glGenVertexArrays(GLsizei n, GLuint *arrays) +{ + webglGenVertexArrays(WEBGL_CAST_I32(n), WEBGL_CAST_I32(*arrays)); +} + +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 = webglGetProgramiv(WEBGL_CAST_I32(program), WEBGL_CAST_I32(pname)); +} + +inline void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) +{ + // TODO: implement + //webglGetShaderInfoLog +} + +inline void glGetShaderiv(GLuint shader, GLenum pname, GLint *params) +{ + *params = webglGetShaderiv(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) +{ + webglShaderSource(WEBGL_CAST_I32(shader), WEBGL_CAST_I32(count), *string, WEBGL_CAST_I32(*length)); +} + +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), 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)); +} + +#undef WEBGL_CAST_I32 diff --git a/wasm_js_implemented_symbols.txt b/wasm_js_implemented_symbols.txt @@ -0,0 +1,34 @@ +webglAttachShader +webglBindBuffer +webglBindVertexArray +webglBlendColor +webglBlendFunc +webglBufferData +webglClear +webglClearColor +webglCompileShader +webglCreateProgram +webglCreateShader +webglDeleteBuffers +webglDeleteShader +webglDeleteVertexArrays +webglDepthMask +webglDisable +webglDrawElements +webglEnable +webglEnableVertexAttribArray +webglGenBuffers +webglGenVertexArrays +webglGetProgramInfoLog +webglGetProgramiv +webglGetShaderInfoLog +webglGetShaderiv +webglGetUniformLocation +webglLinkProgram +webglShaderSource +webglUniform1f +webglUniform1i +webglUniform3f +webglUniformMatrix4fv +webglUseProgram +webglVertexAttribPointer