Untitled
unknown
plain_text
a month ago
18 kB
9
Indexable
#include "game.h" #include "lib/array.h" #include "lib/cglm/cglm.h" #include "lib/common.h" #include "lib/memory.h" #include "lib/stb/stb_image.h" #include "lib/typedefs.h" #include "platform.h" #include "renderer.h" global ArenaAllocator g_temp_allocator; global ArenaAllocator g_allocator; void *global_alloc(size_t size) { return arena_alloc(&g_temp_allocator, size); } void *global_realloc(void *ptr, size_t size) { return arena_realloc(&g_temp_allocator, ptr, size); } typedef struct { RendererBatchId batch_id; mat4 model_matrix; } EntityVisuals; typedef struct { RendererBatchId batch_id; BatchInstanceData_Slice instances; } EntityDrawBatch; slice_define(EntityDrawBatch); typedef struct { struct { union { struct { GameButton left; GameButton right; GameButton up; GameButton down; }; GameButton buttons[4]; }; } input; EntityDrawBatch_Slice batches; vec3 camera_pos; mat4 viewMatrix; mat4 projectionMatrix; mat4 viewProjectionMatrix; } GameState; void update_input_button(GameButton *btn, GameInputEvent event) { if (event.type == EVENT_KEYUP) { btn->released_this_frame = btn->is_pressed; btn->pressed_this_frame = false; btn->is_pressed = false; } else if (event.type == EVENT_KEYDOWN) { btn->pressed_this_frame = !btn->is_pressed; btn->released_this_frame = false; btn->is_pressed = true; } } export void game_init(GameMemory *memory) { // todo: we could just have the platform pass the max memory size g_temp_allocator = arena_from_buffer(memory->temp_buffer, sizeof(memory->temp_buffer)); assert(g_temp_allocator.buffer); u64 heap_start = cast(u64) platform_get_heap_base(); u64 permanent_memory_start = heap_start + sizeof(GameMemory); u64 permanent_memory_size = WASM_MEMORY_SIZE - permanent_memory_start; memory->permanent_memory = cast(uint8 *) permanent_memory_start; g_allocator = arena_from_buffer(memory->permanent_memory, permanent_memory_size); assert(g_allocator.buffer); } export void game_update_and_render(GameMemory *memory) { UNUSED(memory); local_persist PlatformReadFileOp load_sphere_op = -1; local_persist PlatformReadFileOp load_tex_op = -1; local_persist PlatformReadFileOp load_cube_op = -1; local_persist Model3DData *sphere_mesh = NULL; local_persist Model3DData *cube_mesh = NULL; local_persist Image *tex_data = NULL; local_persist GameState *game_state; if (!game_state) { game_state = GAME_ALLOC(GameState); assert(game_state); glm_mat4_identity(game_state->viewMatrix); glm_mat4_identity(game_state->projectionMatrix); glm_mat4_identity(game_state->viewProjectionMatrix); glm_vec3(cast(vec3){0, -0.6, -1.25}, game_state->camera_pos); game_state->batches = cast(EntityDrawBatch_Slice){ .cap = 128, .len = 0, .items = GAME_ALLOC_ARRAY(EntityDrawBatch, 128)}; assert(game_state->batches.items); } if (load_sphere_op < 0) { load_sphere_op = platform_start_read_file("unichan.hmobj"); } if (load_cube_op < 0) { load_cube_op = platform_start_read_file("unichan.hmobj"); } if (load_tex_op < 0) { load_tex_op = platform_start_read_file("unichan.png"); } if (platform_check_read_file(load_sphere_op) == FREADSTATE_COMPLETED) { PlatformFileData data = {}; if (platform_get_file_data(load_sphere_op, &data, &g_temp_allocator)) { sphere_mesh = load_model(data.buffer, data.buffer_len, &g_temp_allocator); } } if (platform_check_read_file(load_cube_op) == FREADSTATE_COMPLETED) { PlatformFileData data = {}; if (platform_get_file_data(load_cube_op, &data, &g_temp_allocator)) { cube_mesh = load_model(data.buffer, data.buffer_len, &g_temp_allocator); } } if (platform_check_read_file(load_tex_op) == FREADSTATE_COMPLETED) { PlatformFileData data = {}; if (platform_get_file_data(load_tex_op, &data, &g_temp_allocator)) { LOG_INFO("Loading png"); // Decode the PNG texture using lodepng_decode32 int x, y, n; u8 *decoded_data = stbi_load_from_memory(data.buffer, data.buffer_len, &x, &y, &n, 0); LOG_INFO("end load png"); if (decoded_data) { // Allocate and populate the tex_data structure tex_data = ARENA_ALLOC(&g_temp_allocator, Image); tex_data->width = x; tex_data->height = y; tex_data->byte_len = data.buffer_len; tex_data->data = (uint8 *)decoded_data; } } } if (sphere_mesh && tex_data && cube_mesh) { for (u32 i = 0; i < sphere_mesh->num_meshes; i++) { MeshData *mesh = &sphere_mesh->meshes[i]; RendererBatchId sphere_batch_id = renderer_create_batch(mesh, tex_data); EntityDrawBatch sphere_batch = cast(EntityDrawBatch){ .batch_id = sphere_batch_id, .instances = {.cap = 128, .len = 0, .items = GAME_ALLOC_ARRAY(BatchInstanceData, 128)}}; slice_append(game_state->batches, sphere_batch); } cube_mesh = NULL; sphere_mesh = NULL; tex_data = NULL; } // input stuff GameInputEvents *input_events = &memory->input_events; for (u32 i = 0; i < input_events->len; i++) { GameInputEvent e = input_events->events[i]; if (e.key.type == KEY_D) { update_input_button(&game_state->input.right, e); } else if (e.key.type == KEY_A) { update_input_button(&game_state->input.left, e); } else if (e.key.type == KEY_W) { update_input_button(&game_state->input.up, e); } else if (e.key.type == KEY_S) { update_input_button(&game_state->input.down, e); } } f32 dt = memory->time.dt; f32 cam_speed = 2; if (game_state->input.left.is_pressed) { game_state->camera_pos[0] += cam_speed * dt; } if (game_state->input.right.is_pressed) { game_state->camera_pos[0] -= cam_speed * dt; } if (game_state->input.up.is_pressed) { game_state->camera_pos[1] -= cam_speed * dt; } if (game_state->input.down.is_pressed) { game_state->camera_pos[1] += cam_speed * dt; } // render glm_mat4_identity(game_state->viewMatrix); glm_translate(game_state->viewMatrix, game_state->camera_pos); f32 aspect = cast(f32) memory->canvas.width / memory->canvas.height; f32 fov = 60; glm_perspective(glm_rad(fov), aspect, 0.1, 100, game_state->projectionMatrix); glm_mat4_mul(game_state->projectionMatrix, game_state->viewMatrix, game_state->viewProjectionMatrix); renderer_set_uniforms(game_state->viewMatrix, game_state->projectionMatrix, game_state->viewProjectionMatrix); // batch stuff for (u32 i = 0; i < game_state->batches.len; i++) { EntityDrawBatch *batch = &game_state->batches.items[i]; batch->instances.len = 0; BatchInstanceData m1 = {}; // BatchInstanceData m2 = {}; // BatchInstanceData m3 = {}; glm_mat4_identity(m1.model_matrix); // glm_mat4_identity(m2.model_matrix); // glm_mat4_identity(m3.model_matrix); // glm_translate_x(m1.model_matrix, 1.5 * (i + 1)); // glm_rotate_x(m1.model_matrix, glm_rad(30), m1.model_matrix); // glm_rotate_y(m1.model_matrix, glm_rad(-45), m1.model_matrix); // glm_translate_x(m2.model_matrix, -1.5 * (i + 1)); // glm_translate_y(m3.model_matrix, 1 * (i + 1)); slice_append(batch->instances, m1); // slice_append(batch->instances, m2); // slice_append(batch->instances, m3); batch_set_instance_data(batch->batch_id, batch->instances.items, batch->instances.len); } // clean input for (u32 i = 0; i < ARRAY_SIZE(game_state->input.buttons); i++) { game_state->input.buttons[i].released_this_frame = false; game_state->input.buttons[i].pressed_this_frame = false; } LOG_INFO("\nTemp Memory %/% mb\nMain Memory %/%", FMT_UINT(BYTES_TO_MB(g_temp_allocator.offset)), FMT_UINT(BYTES_TO_MB(g_temp_allocator.capacity)), FMT_UINT(BYTES_TO_MB(g_allocator.offset)), FMT_UINT(BYTES_TO_MB(g_allocator.capacity))); arena_reset(&g_temp_allocator); } renderer.c (simplified) #include "renderer.h" #include "lib/memory.h" #include "lib/typedefs.h" #include <string.h> // todo: gotta handle cross platform compilation #if defined(__wasm__) extern RendererBatchId _renderer_create_batch(f32 *verts, u32 len_verts, u32 *indices, u32 len_indices, u8 *texture, u32 len_texture, u32 width_texture, u32 height_texture); extern bool32 _renderer_update_batch(RendererBatchId batch_id, f32 *instances, u32 float_count); extern void _renderer_set_uniforms(f32 *view_matrix, f32 *projection_matrix, f32 *view_projection_matrix); RendererBatchId renderer_create_batch(const MeshData *mesh, Image *texture) { return _renderer_create_batch(mesh->vertex_buffer, mesh->len_vertex_buffer, mesh->indices, mesh->len_indices, texture->data, texture->byte_len, texture->width, texture->height); } bool32 batch_set_instance_data(RendererBatchId batch_id, BatchInstanceData *instances, u32 len) { u32 float_count = len * sizeof(BatchInstanceData) / sizeof(float32); return _renderer_update_batch(batch_id, (f32 *)instances, float_count); } void renderer_set_uniforms(mat4 view_matrix, mat4 projection_matrix, mat4 view_projection_matrix) { _renderer_set_uniforms(cast(f32 *) view_matrix, cast(f32 *) projection_matrix, cast(f32 *) view_projection_matrix); } #endif typedef struct { u32 len; const uint8 *bytes; u32 cur_offset; } BinaryReader; bool32 read_u32(BinaryReader *reader, u32 *v) { u32 end_offset = reader->cur_offset + sizeof(u32); bool32 within_range = end_offset <= reader->len; assert(within_range); if (!within_range) { return false; } memcpy(v, &reader->bytes[reader->cur_offset], sizeof(u32)); reader->cur_offset = end_offset; return true; } bool32 read_f32_array(BinaryReader *reader, f32 *arr, u32 len) { u32 end_offset = reader->cur_offset + sizeof(f32) * len; bool32 within_range = end_offset <= reader->len; assert(within_range); if (!within_range) { return false; } memcpy(arr, &reader->bytes[reader->cur_offset], len * sizeof(f32)); reader->cur_offset = end_offset; return true; } bool32 read_u32_array(BinaryReader *reader, u32 *arr, u32 len) { u32 end_offset = reader->cur_offset + sizeof(u32) * len; bool32 within_range = end_offset <= reader->len; assert(within_range); if (!within_range) { return false; } memcpy(arr, &reader->bytes[reader->cur_offset], len * sizeof(u32)); reader->cur_offset = end_offset; return true; } Model3DData *load_model(const uint8 *binary_data, u32 data_len, ArenaAllocator *allocator) { if (binary_data == NULL || data_len == 0 || allocator == NULL) { return NULL; // Invalid input } BinaryReader reader = { .bytes = binary_data, .len = data_len, .cur_offset = 0}; Model3DData *model = ARENA_ALLOC(allocator, Model3DData); if (model == NULL) { return NULL; } read_u32(&reader, &model->version); read_u32(&reader, &model->num_meshes); model->meshes = ARENA_ALLOC_ARRAY(allocator, MeshData, model->num_meshes); if (model->meshes == NULL) { return NULL; } // Read mesh data for (u32 i = 0; i < model->num_meshes; i++) { MeshData *mesh = &model->meshes[i]; read_u32(&reader, &mesh->len_vertices); read_u32(&reader, &mesh->len_vertex_buffer); mesh->vertex_buffer = ARENA_ALLOC_ARRAY(allocator, f32, mesh->len_vertex_buffer); if (mesh->vertex_buffer == NULL) { return NULL; } read_f32_array(&reader, mesh->vertex_buffer, mesh->len_vertex_buffer); read_u32(&reader, &mesh->len_indices); mesh->indices = ARENA_ALLOC_ARRAY(allocator, u32, mesh->len_indices); if (mesh->indices == NULL) { return NULL; } read_u32_array(&reader, mesh->indices, mesh->len_indices); } assert(reader.cur_offset == reader.len); return model; } game.ts (simplified) function _renderer_create_batch( vertPtr: number, vertLen: number, indexPtr: number, indexLen: number, texPtr: number, texLen: number, texWidth: number, texHeight: number, ) { const verticesView = new Float32Array( wasmMemory.memory.buffer, vertPtr, vertLen, ); const indicesView = new Uint32Array( wasmMemory.memory.buffer, indexPtr, indexLen, ); const mesh: Mesh = { vertices: verticesView, indices: indicesView, }; const batch = StandardMaterialBatch.new( renderer.gl, renderer.standardPipeline.program, mesh, ); texLen = texWidth * texHeight * 4; const texData = new Uint8Array(wasmMemory.memory.buffer, texPtr, texLen); batch.setTextureFromRGBA(renderer.gl, texData, texWidth, texHeight); renderer.standardPipeline.batches.push(batch); return renderer.standardPipeline.batches.length; } function _renderer_update_batch( batchId: number, instanceDataPtr: number, instanceDataLen: number, ) { const batch = renderer.standardPipeline.batches[batchId - 1]; if (!batch) { return false; } const instanceData = wasmMemory.loadF32Array( instanceDataPtr, instanceDataLen, ); batch.updateInstanceData(renderer.gl, instanceData); } function _renderer_set_uniforms( viewMatrixPtr: number, projectionMatrixPtr: number, viewProjectionMatrixPtr: number, ) { renderer.viewMatrix = wasmMemory.loadF32Array(viewMatrixPtr, 16); renderer.projectionMatrix = wasmMemory.loadF32Array( projectionMatrixPtr, 16, ); renderer.viewProjectionMatrix = wasmMemory.loadF32Array( viewProjectionMatrixPtr, 16, ); } const wasmModule = await WebAssembly.instantiate( await response.arrayBuffer(), { env: { memory, _platform_log_info, _platform_log_warn, _platform_log_error, _platform_start_read_file, _platform_check_read_file, _platform_get_file_size, _platform_get_file_data, _renderer_create_batch, _renderer_update_batch, _renderer_set_uniforms, }, }, ); renderer.ts (simplified) export class Renderer { gl: WebGL2RenderingContext; canvas: HTMLCanvasElement; viewMatrix: mat4; projectionMatrix: mat4; viewProjectionMatrix: mat4; //hdri frameBuffer: FrameBuffer; //frame buffer quad quadVAO: WebGLVertexArrayObject; quadVBO: WebGLBuffer; presentProgram: WebGLProgram; standardPipeline: StandardMaterialPipeline; private resizeOnNextDraw: boolean = false; static new(canvas: HTMLCanvasElement) { //@ts-ignore const renderer = new Renderer(); const gl = canvas.getContext("webgl2", { antialias: true, powerPreference: "high-performance", }); if (!gl) { throw new Error("WebGL2 not supported"); } //make sure canvas is the right size before proceeding const dpi = window.devicePixelRatio || 1; canvas.width = window.innerWidth * dpi; canvas.height = window.innerHeight * dpi; renderer.frameBuffer = FrameBuffer.new(gl); //setup present shader { const quadVAO = gl.createVertexArray(); gl.bindVertexArray(quadVAO); const quadVBO = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, quadVBO); // prettier-ignore const quadVertices = new Float32Array([ -1, -1, 0, 0, 1, -1, 1, 0, -1, 1, 0, 1, 1, 1, 1, 1 ]); gl.bufferData(gl.ARRAY_BUFFER, quadVertices, gl.STATIC_DRAW); //pos gl.enableVertexAttribArray(0); gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 16, 0); //uv gl.enableVertexAttribArray(1); gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 16, 8); gl.bindVertexArray(null); const presentProgram = createProgram( gl, vertexShaderSource, fragmentShaderSource, ); renderer.quadVAO = quadVAO; renderer.quadVBO = quadVBO; renderer.presentProgram = presentProgram; } renderer.gl = gl; renderer.canvas = canvas; renderer.resize(); return renderer; } resize() { this.resizeOnNextDraw = true; } render() { const { gl, viewProjectionMatrix, standardPipeline, frameBuffer } = this; if (gl.isContextLost()) { console.error("Context lost"); return; } if (this.resizeOnNextDraw) { const { canvas, gl } = this; const dpi = window.devicePixelRatio || 1; canvas.width = window.innerWidth * dpi; canvas.height = window.innerHeight * dpi; frameBuffer.resize(gl); this.resizeOnNextDraw = false; } //render to the frame buffer frameBuffer.bind(gl); gl.clearColor(0.1, 0.1, 0.1, 1); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.enable(gl.DEPTH_TEST); standardPipeline.render(gl, viewProjectionMatrix); frameBuffer.unbind(gl); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); const { presentProgram, quadVAO } = this; gl.useProgram(presentProgram); gl.uniform1i(gl.getUniformLocation(presentProgram, "hdrTexture"), 0); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, frameBuffer.frameTexture); gl.bindVertexArray(quadVAO); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); gl.bindVertexArray(null); gl.bindTexture(gl.TEXTURE_2D, null); } } standard_pipeline.ts (simplified) //batch rendering code render(gl: WebGL2RenderingContext) { const { vao, texture, instanceCount, textureUniformLoc, indexCount, glIndexType, } = this; gl.bindVertexArray(vao); gl.uniform1i(textureUniformLoc, 0); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, texture); gl.drawElementsInstanced( gl.TRIANGLES, indexCount, glIndexType, 0, instanceCount, ); gl.bindVertexArray(null); gl.bindTexture(gl.TEXTURE_2D, null); }
Editor is loading...
Leave a Comment