Initial boilerplate

This commit is contained in:
Clément Pons 2025-03-25 15:10:19 +01:00
commit 79f8d6d4dc
5 changed files with 3179 additions and 0 deletions

1
build.bat Normal file
View File

@ -0,0 +1 @@
emcc -O2 src/main.c -o app.html -sUSE_WEBGL2 -sASSERTIONS -sWASM_BIGINT -pthread -I../sokol --shell-file=shell.html --closure 1

42
shell.html Normal file
View File

@ -0,0 +1,42 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"/>
<title>Code</title>
<style>
body { margin: 0; background-color: black }
.game {
position: absolute;
top: 0px;
left: 0px;
margin: 0px;
border: 0;
width: 100%;
height: 100%;
overflow: hidden;
display: block;
image-rendering: optimizeSpeed;
image-rendering: -moz-crisp-edges;
image-rendering: -o-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: optimize-contrast;
image-rendering: crisp-edges;
image-rendering: pixelated;
-ms-interpolation-mode: nearest-neighbor;
}
</style>
</head>
<body>
<canvas class="game" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<script type='text/javascript'>
var Module = {
preRun: [],
print: (...args) => console.log('[stdout]: ' + args.join(' ')),
printErr: (...args) => console.log('[stderr]: ' + args.join(' ')),
};
window.onerror = (event) => console.log('[onerror]: ' + event.message);
</script>
{{{ SCRIPT }}}
</body>
</html>

2884
src/HandmadeMath.h Normal file

File diff suppressed because it is too large Load Diff

80
src/emsc.h Normal file
View File

@ -0,0 +1,80 @@
#pragma once
/* common emscripten platform helper functions */
#include <emscripten/emscripten.h>
#include <emscripten/html5.h>
#include <GLES3/gl3.h>
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmissing-field-initializers"
#pragma clang diagnostic ignored "-Wmissing-braces"
#endif
static const char* _emsc_canvas_name = 0;
static int _emsc_sample_count = 0;
static double _emsc_width = 0;
static double _emsc_height = 0;
static GLint _emsc_framebuffer = 0;
enum {
EMSC_NONE = 0,
EMSC_ANTIALIAS = (1<<1)
};
/* track CSS element size changes and update the WebGL canvas size */
static EM_BOOL _emsc_size_changed(int event_type, const EmscriptenUiEvent* ui_event, void* user_data) {
(void)event_type;
(void)ui_event;
(void)user_data;
emscripten_get_element_css_size(_emsc_canvas_name, &_emsc_width, &_emsc_height);
emscripten_set_canvas_element_size(_emsc_canvas_name, _emsc_width, _emsc_height);
return true;
}
/* initialize WebGL context and canvas */
void emsc_init(const char* canvas_name, int flags) {
_emsc_canvas_name = canvas_name;
emscripten_get_element_css_size(canvas_name, &_emsc_width, &_emsc_height);
emscripten_set_canvas_element_size(canvas_name, _emsc_width, _emsc_height);
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _emsc_size_changed);
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx;
EmscriptenWebGLContextAttributes attrs;
emscripten_webgl_init_context_attributes(&attrs);
attrs.antialias = flags & EMSC_ANTIALIAS;
attrs.majorVersion = 2;
_emsc_sample_count = (flags & EMSC_ANTIALIAS) ? 4 : 1;
ctx = emscripten_webgl_create_context(canvas_name, &attrs);
emscripten_webgl_make_context_current(ctx);
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&_emsc_framebuffer);
}
int emsc_width(void) {
return (int) _emsc_width;
}
int emsc_height(void) {
return (int) _emsc_height;
}
sg_environment emsc_environment(void) {
return (sg_environment){
.defaults = {
.color_format = SG_PIXELFORMAT_RGBA8,
.depth_format = SG_PIXELFORMAT_DEPTH_STENCIL,
.sample_count = _emsc_sample_count,
}
};
}
sg_swapchain emsc_swapchain(void) {
return (sg_swapchain) {
.width = (int)_emsc_width,
.height = (int)_emsc_height,
.sample_count = _emsc_sample_count,
.color_format = SG_PIXELFORMAT_RGBA8,
.depth_format = SG_PIXELFORMAT_DEPTH_STENCIL,
.gl = {
.framebuffer = (uint32_t)_emsc_framebuffer,
}
};
}

172
src/main.c Normal file
View File

@ -0,0 +1,172 @@
//------------------------------------------------------------------------------
// cube-emsc.c
// Shader uniforms updates.
//------------------------------------------------------------------------------
#define HANDMADE_MATH_IMPLEMENTATION
#define HANDMADE_MATH_NO_SSE
#include "HandmadeMath.h"
#define SOKOL_IMPL
#define SOKOL_GLES3
#include "sokol_gfx.h"
#include "sokol_log.h"
#include "emsc.h"
static struct {
float rx, ry;
sg_pipeline pip;
sg_bindings bind;
sg_pass_action pass_action;
} state = {
.pass_action.colors[0] = { .load_action = SG_LOADACTION_CLEAR, .clear_value = { 0.0f, 0.0f, 0.0f, 1.0f } }
};
typedef struct {
hmm_mat4 mvp;
} params_t;
static EM_BOOL draw(double time, void* userdata);
int main() {
// setup WebGL context
emsc_init("#canvas", EMSC_ANTIALIAS);
// setup sokol_gfx
sg_setup(&(sg_desc){
.environment = emsc_environment(),
.logger.func = slog_func
});
assert(sg_isvalid());
// cube vertex buffer
float vertices[] = {
-1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
-1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
-1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
1.0, -1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
-1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
-1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 1.0,
-1.0, 1.0, -1.0, 0.0, 0.0, 1.0, 1.0,
-1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0,
-1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0,
1.0, -1.0, -1.0, 1.0, 0.5, 0.0, 1.0,
1.0, 1.0, -1.0, 1.0, 0.5, 0.0, 1.0,
1.0, 1.0, 1.0, 1.0, 0.5, 0.0, 1.0,
1.0, -1.0, 1.0, 1.0, 0.5, 0.0, 1.0,
-1.0, -1.0, -1.0, 0.0, 0.5, 1.0, 1.0,
-1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 1.0,
1.0, -1.0, 1.0, 0.0, 0.5, 1.0, 1.0,
1.0, -1.0, -1.0, 0.0, 0.5, 1.0, 1.0,
-1.0, 1.0, -1.0, 1.0, 0.0, 0.5, 1.0,
-1.0, 1.0, 1.0, 1.0, 0.0, 0.5, 1.0,
1.0, 1.0, 1.0, 1.0, 0.0, 0.5, 1.0,
1.0, 1.0, -1.0, 1.0, 0.0, 0.5, 1.0
};
state.bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
.data = SG_RANGE(vertices)
});
// create an index buffer for the cube
uint16_t indices[] = {
0, 1, 2, 0, 2, 3,
6, 5, 4, 7, 6, 4,
8, 9, 10, 8, 10, 11,
14, 13, 12, 15, 14, 12,
16, 17, 18, 16, 18, 19,
22, 21, 20, 23, 22, 20
};
state.bind.index_buffer = sg_make_buffer(&(sg_buffer_desc){
.type = SG_BUFFERTYPE_INDEXBUFFER,
.data = SG_RANGE(indices)
});
// create shader
sg_shader shd = sg_make_shader(&(sg_shader_desc){
.vertex_func.source =
"#version 300 es\n"
"uniform mat4 mvp;\n"
"in vec4 position;\n"
"in vec4 color0;\n"
"out vec4 color;\n"
"void main() {\n"
" gl_Position = mvp * position;\n"
" color = color0;\n"
"}\n",
.fragment_func.source =
"#version 300 es\n"
"precision mediump float;\n"
"in vec4 color;\n"
"out vec4 frag_color;\n"
"void main() {\n"
" frag_color = color;\n"
"}\n",
.attrs = {
[0].glsl_name = "position",
[1].glsl_name = "color0"
},
.uniform_blocks[0] = {
.stage = SG_SHADERSTAGE_VERTEX,
.size = sizeof(params_t),
.glsl_uniforms = {
[0] = { .glsl_name = "mvp", .type = SG_UNIFORMTYPE_MAT4 }
}
},
});
// create pipeline object
state.pip = sg_make_pipeline(&(sg_pipeline_desc){
.layout = {
// test to provide buffer stride, but no attr offsets
.buffers[0].stride = 28,
.attrs = {
[0].format=SG_VERTEXFORMAT_FLOAT3,
[1].format=SG_VERTEXFORMAT_FLOAT4
}
},
.shader = shd,
.index_type = SG_INDEXTYPE_UINT16,
.depth = {
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true
},
.cull_mode = SG_CULLMODE_BACK
});
// hand off control to browser loop
emscripten_request_animation_frame_loop(draw, 0);
return 0;
}
// draw one frame
static EM_BOOL draw(double time, void* userdata) {
(void)time; (void)userdata;
// compute model-view-projection matrix for vertex shader
hmm_mat4 proj = HMM_Perspective(60.0f, (float)emsc_width()/(float)emsc_height(), 0.01f, 10.0f);
hmm_mat4 view = HMM_LookAt(HMM_Vec3(0.0f, 1.5f, 6.0f), HMM_Vec3(0.0f, 0.0f, 0.0f), HMM_Vec3(0.0f, 1.0f, 0.0f));
hmm_mat4 view_proj = HMM_MultiplyMat4(proj, view);
state.rx += 1.0f; state.ry += 2.0f;
hmm_mat4 rxm = HMM_Rotate(state.rx, HMM_Vec3(1.0f, 0.0f, 0.0f));
hmm_mat4 rym = HMM_Rotate(state.ry, HMM_Vec3(0.0f, 1.0f, 0.0f));
hmm_mat4 model = HMM_MultiplyMat4(rxm, rym);
const params_t vs_params = {
.mvp = HMM_MultiplyMat4(view_proj, model)
};
// ...and draw
sg_begin_pass(&(sg_pass){ .action = state.pass_action, .swapchain = emsc_swapchain() });
sg_apply_pipeline(state.pip);
sg_apply_bindings(&state.bind);
sg_apply_uniforms(0, &SG_RANGE(vs_params));
sg_draw(0, 36, 1);
sg_end_pass();
sg_commit();
return EM_TRUE;
}