transparent-cube

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

commit 98c0b5fb0a34fc3eb635b27d19cb49b6e71f2bb3
parent 2a92d937febe092701d1a6aa128961f9c428bf94
Author: amin <dev@aminmesbah.com>
Date:   Sat, 28 Jul 2018 04:45:12 +0000

Port to Emscripten

Emscripten [1] allows us to compile c code into wasm, using webGL for
rendering in the browser.

http://kripken.github.io/emscripten-site/

FossilOrigin-Name: aa59e204b0e70e981d08d17f30f77545f803d1882b80063052dbe722fb9a7c38
Diffstat:
MMakefile | 16+++++++++++++---
Mshader/pyramid_f.glsl | 4+++-
Mshader/pyramid_v.glsl | 2+-
Msrc/game.c | 8++++++--
Msrc/game.h | 5+++++
Asrc/platform_emscripten.c | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/platform_emscripten.h | 22++++++++++++++++++++++
Msrc/shader.c | 8+++++++-
Msrc/shader.h | 3+--
9 files changed, 157 insertions(+), 10 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,6 +1,6 @@ CC = clang CFLAGS = -std=c99 -Ilib -Wall -Wextra -Wshadow -Wswitch-enum -Wno-unused-parameter -Wno-missing-braces -LDFLAGS = -ldl -lglfw -lGL -lGLEW -lm +LDFLAGS = -ldl -lglfw -lGL -lm SRC_FILES = game.c glad.c glmth.c platform_linux.c shader.c SRC = $(addprefix src/, $(SRC_FILES)) @@ -20,11 +20,17 @@ RELLIB = $(RELDIR)/$(LIB_NAME) RELLIBTMP = $(RELLIB).tmp RELCFLAGS = -DPLATFORM_HOTLOAD_GAME_CODE -O2 -Os -.PHONY: default all build_debug build_lib build_release clean debug dir_debug dir_release memcheck run todo +EMS_FILES = game.c glmth.c platform_emscripten.c shader.c +EMSSRC = $(addprefix src/, $(EMS_FILES)) +EMSDIR = build/emscripten +EMSEXE = $(EMSDIR)/$(EXE_FILE).html +EMSCFLAGS = --preload-file shader -s USE_GLFW=3 -s USE_WEBGL2=1 + +.PHONY: default all build_debug build_lib build_release clean debug dir_debug dir_release emscripten memcheck run todo default: build_release -all: build_debug build_release +all: build_debug build_release emscripten build_debug: dir_debug $(CC) $(CFLAGS) $(DBGCFLAGS) $(SRC) -o $(DBGEXE) $(LDFLAGS) @@ -54,6 +60,10 @@ memcheck: build_debug run: build_release ./$(RELEXE) +emscripten: + @mkdir -p $(EMSDIR) + emcc $(CFLAGS) $(EMSCFLAGS) -O2 -Os $(EMSSRC) -o $(EMSEXE) $(LDFLAGS) + todo: @grep -FIR --colour=never --ignore-case --line-number todo src/ \ | sed -re 's/^([^:]+):[[:space:]]*(.*)/\1\x01\2/' \ diff --git a/shader/pyramid_f.glsl b/shader/pyramid_f.glsl @@ -1,4 +1,6 @@ -#version 330 core +#version 300 es + +precision highp float; uniform vec3 pyramid_color; diff --git a/shader/pyramid_v.glsl b/shader/pyramid_v.glsl @@ -1,4 +1,4 @@ -#version 330 core +#version 300 es layout (location = 0) in vec3 a_position; diff --git a/src/game.c b/src/game.c @@ -3,7 +3,11 @@ #include <assert.h> #include <math.h> -#include <glad/glad.h> +#ifdef __EMSCRIPTEN__ +#include <GLES3/gl3.h> +#else +#include "glad/glad.h" +#endif #ifdef PLATFORM_HOTLOAD_GAME_CODE @@ -108,7 +112,7 @@ void game_update_and_render(struct GameState *game_state, float dt, uint32_t scr glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); shader_use(&game_state->pyramid_shader); diff --git a/src/game.h b/src/game.h @@ -1,7 +1,12 @@ #ifndef GAME_H #define GAME_H +#ifdef __EMSCRIPTEN__ +#include <GLES3/gl3.h> +#else #include "glad/glad.h" +#endif + #include "glmth.h" #include "shader.h" diff --git a/src/platform_emscripten.c b/src/platform_emscripten.c @@ -0,0 +1,99 @@ +#include "platform_emscripten.h" + +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> +#include <time.h> + +#include <emscripten.h> + +#define GLFW_INCLUDE_ES3 +#include <GLFW/glfw3.h> + + +void error_callback(int error, const char *description); +void framebuffer_size_callback(GLFWwindow *window, int width, int height); +void platform_main_loop(void *data); + + +int main(void) +{ + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + { + fprintf(stderr, "GLFW initialization failed\n"); + return -1; + } + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); + + GLFWwindow* window = glfwCreateWindow(PLATFORM_SCR_WIDTH, PLATFORM_SCR_HEIGHT, "Quaternion Demo", NULL, NULL); + if (!window) + { + fprintf(stderr, "GLFW window creation failed\n"); + glfwTerminate(); + return -1; + } + glfwMakeContextCurrent(window); + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + + struct GameState game_state = {0}; + game_init(&game_state, PLATFORM_SCR_WIDTH, PLATFORM_SCR_HEIGHT); + + struct LoopArgs loop_args = { + .counter = 0, + .previous_s = 0.0f, + .lag = 0.0f, + .game_state = &game_state, + .window = window, + }; + void *args = &loop_args; + + emscripten_set_main_loop_arg(platform_main_loop, args, 0, 1); + + game_cleanup(&game_state); + + glfwDestroyWindow(window); + glfwTerminate(); + + return 0; +} + + +void platform_main_loop(void *data) +{ + struct LoopArgs *args = (struct LoopArgs *)(data); + + // glfwGetTimerValue() is not currently implemented in Emscripten. + double current_s = glfwGetTime(); + double elapsed_s = current_s - args->previous_s; + args->previous_s = current_s; + args->lag += elapsed_s; + //printf("%f, %f\n", elapsed_s, args->lag); + + int32_t framebuffer_width = PLATFORM_SCR_WIDTH; + int32_t framebuffer_height = PLATFORM_SCR_HEIGHT; + glfwGetFramebufferSize(args->window, &framebuffer_width, &framebuffer_height); + + // TODO: args->lag just increases forever. Fix that. + game_update_and_render(args->game_state, args->lag, framebuffer_width, framebuffer_height); + + glfwSwapBuffers(args->window); + glfwPollEvents(); +} + + +void error_callback(int error, const char *description) +{ + fprintf(stderr, "Error: %s\n", description); +} + + +void framebuffer_size_callback(GLFWwindow *window, int width, int height) +{ + glViewport(0, 0, width, height); +} diff --git a/src/platform_emscripten.h b/src/platform_emscripten.h @@ -0,0 +1,22 @@ +#ifndef PLATFORM_EMSCRIPTEN_H +#define PLATFORM_EMSCRIPTEN_H + +#include <stdbool.h> +#include <time.h> + +#include <GLFW/glfw3.h> +#include "game.h" + +#define PLATFORM_SCR_WIDTH 600 +#define PLATFORM_SCR_HEIGHT 600 + +struct LoopArgs +{ + int counter; + struct GameState *game_state; + double previous_s; + double lag; + GLFWwindow* window; +}; + +#endif // PLATFORM_EMSCRIPTEN_H diff --git a/src/shader.c b/src/shader.c @@ -1,5 +1,11 @@ #include "shader.h" +#ifdef __EMSCRIPTEN__ +#include <GLES3/gl3.h> +#else +#include "glad/glad.h" +#endif + char *read_file(char *file_path) { @@ -36,7 +42,7 @@ char *read_file(char *file_path) } -struct Shader shader_compile(GLchar *vertex_path, GLchar *fragment_path) +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); diff --git a/src/shader.h b/src/shader.h @@ -1,7 +1,6 @@ #ifndef SHADER_H #define SHADER_H -#include <glad/glad.h> // get opengl headers #include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -14,7 +13,7 @@ struct Shader }; char *read_file(char *file_path); -struct Shader shader_compile(GLchar *vertex_path, GLchar *fragment_path); +struct Shader shader_compile(char *vertex_path, char *fragment_path); 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);