From c5370a46141cfdea381b494f26f5927d9f241e34 Mon Sep 17 00:00:00 2001 From: Emil Kosz Date: Tue, 3 Jun 2025 19:29:36 +0200 Subject: [PATCH] Entity system, generate some trees around player, etc --- game/Makefile | 8 ++--- game/res/shaders/billboard.vert | 3 +- game/res/shaders/chunk.frag | 1 - game/res/shaders/default.frag | 2 +- game/res/shaders/default.vert | 3 +- game/src/chunk/chunk.c | 30 ++++++++++++++++- game/src/chunk/chunk.h | 4 +++ game/src/chunk/manager.c | 3 ++ game/src/entity/#templates.c# | 25 ++++++++++++++ game/src/entity/.#templates.c | 1 + game/src/entity/entity.h | 20 +++++------ game/src/entity/manager.c | 60 +++++++++++++++++++++++++++++++-- game/src/entity/manager.h | 6 ++-- game/src/entity/meshes.c | 8 ++--- game/src/entity/meshes.h | 1 - game/src/entity/templates.c | 32 ++++++++++++++++++ game/src/entity/templates.h | 13 +++++++ game/src/graphics.c | 42 +++++++++++------------ game/src/main.c | 5 ++- game/src/resources.c | 22 ++++++------ game/src/resources.h | 12 +++---- 21 files changed, 229 insertions(+), 72 deletions(-) create mode 100644 game/src/entity/#templates.c# create mode 120000 game/src/entity/.#templates.c create mode 100644 game/src/entity/templates.c create mode 100644 game/src/entity/templates.h diff --git a/game/Makefile b/game/Makefile index 54615fd..1bf32dc 100644 --- a/game/Makefile +++ b/game/Makefile @@ -1,11 +1,11 @@ SRC=src/main.c src/program.c src/resources.c src/camera.c src/chunk/chunk.c \ src/chunk/mesh.c src/flags.c src/texture.c src/chunk/manager.c \ src/event/manager.c src/event/handlers.c src/state.c src/graphics.c \ - src/entity/meshes.c src/entity/manager.c + src/entity/meshes.c src/entity/manager.c src/entity/templates.c OBJ=$(SRC:.c=.o) -CFLAGS=-pg -Werror -Wextra -Wall `pkg-config --cflags sdl3 sdl3-image cglm gl glib-2.0` -LDFLAGS=`pkg-config --libs sdl3 sdl3-image cglm gl glib-2.0` -lm +CFLAGS=-g -pg -Werror -Wextra -Wall `pkg-config --cflags sdl3 sdl3-image cglm gl glib-2.0` +LDFLAGS=-g -pg -lm `pkg-config --libs sdl3 sdl3-image cglm gl glib-2.0` TARGET=wschod CC=gcc LD=gcc @@ -21,4 +21,4 @@ $(TARGET): $(OBJ) $(CC) -c $^ -o $@ $(CFLAGS) clean: - $(RM) $(TARGET) $(OBJ) + $(RM) gmon.out $(TARGET) $(OBJ) diff --git a/game/res/shaders/billboard.vert b/game/res/shaders/billboard.vert index 6b02501..18edcdd 100644 --- a/game/res/shaders/billboard.vert +++ b/game/res/shaders/billboard.vert @@ -4,8 +4,9 @@ attribute vec4 position; uniform mat4 view; uniform mat4 projection; +uniform mat4 model; void main () { - gl_Position = projection * view * position; + gl_Position = projection * view * model * position; } diff --git a/game/res/shaders/chunk.frag b/game/res/shaders/chunk.frag index 69c06c5..d63ebe6 100644 --- a/game/res/shaders/chunk.frag +++ b/game/res/shaders/chunk.frag @@ -18,6 +18,5 @@ void main () result = mix (result, texture2D (materials[3], f_uv), f_material[2]); result = mix (result, texture2D (materials[4], f_uv), f_material[3]); - result = result * (f_height / 5.0); gl_FragColor = result; } diff --git a/game/res/shaders/default.frag b/game/res/shaders/default.frag index 18447de..07bea22 100644 --- a/game/res/shaders/default.frag +++ b/game/res/shaders/default.frag @@ -8,7 +8,7 @@ varying vec2 f_uv; void main () { - if(texture2D (texture, f_uv).a < 0.1) + if(texture2D (texture, f_uv).a < 0.2) { discard; } diff --git a/game/res/shaders/default.vert b/game/res/shaders/default.vert index ebe8591..21ce9f3 100644 --- a/game/res/shaders/default.vert +++ b/game/res/shaders/default.vert @@ -5,11 +5,12 @@ attribute vec2 uv; uniform mat4 view; uniform mat4 projection; +uniform mat4 model; varying vec2 f_uv; void main () { f_uv = uv; - gl_Position = projection * view * position; + gl_Position = projection * view * model * position; } diff --git a/game/src/chunk/chunk.c b/game/src/chunk/chunk.c index 1826c00..b6ef6ad 100644 --- a/game/src/chunk/chunk.c +++ b/game/src/chunk/chunk.c @@ -2,6 +2,9 @@ #include +extern GHashTable *entity_templates; +extern GSequence *entity_manager; + Chunk * Chunk_new () { @@ -11,6 +14,13 @@ Chunk_new () return chunk; } +/* Example function for a tick for an entity. */ +void +tree_grow (Entity *entity) +{ + entity->scale[1] += 0.01; +} + void Chunk_random (Chunk *chunk, int xoffset, int zoffset) { @@ -20,10 +30,28 @@ Chunk_random (Chunk *chunk, int xoffset, int zoffset) { chunk->heightmap[INDEX (x, z)] = (glm_perlin_vec2 - ((vec2) { (x + xoffset + 0.5) / 10.0, (z + zoffset + 0.5) / 10.0 }) + 0.6) * 5; + ((vec2) { (x + xoffset + 0.5) / 10.0, (z + zoffset + 0.5) / 10.0 }) + 0.6) * 10; chunk->materialmap[INDEX (x, z)] = (int) (glm_perlin_vec2 ((vec2) { (x + xoffset + 0.5) / 20.0, (z + zoffset + 0.5) / 20.0 }) + 0.6 * 5) % 5; + + if (rand() % 100 < 2) + { + Entity *tree = malloc (sizeof (Entity)); + + *tree = (Entity) { + .type = ENTITY_SOLID, + .mesh_type = ENTITY_MESH_X, + .origin = ENTITY_BOTTOM, + .position = { (float) x + xoffset, chunk->heightmap[INDEX (x, z)], (float) z + zoffset }, + .scale = { 3.0, 10.0, 3.0 }, + .rotation = (rand () % 180) * (M_PI / 180.0), + .texture = "tree0", + .on_tick = tree_grow + }; + + EM_ENTADD (tree); + } } } } diff --git a/game/src/chunk/chunk.h b/game/src/chunk/chunk.h index 9115a6b..4412a5e 100644 --- a/game/src/chunk/chunk.h +++ b/game/src/chunk/chunk.h @@ -2,9 +2,13 @@ #include +#include +#include #include #include "../util.h" +#include "../entity/templates.h" +#include "../entity/manager.h" #define CHUNK_DIM 32 #define CHUNK_SIZE (CHUNK_DIM*CHUNK_DIM) diff --git a/game/src/chunk/manager.c b/game/src/chunk/manager.c index e6149b2..756bc61 100644 --- a/game/src/chunk/manager.c +++ b/game/src/chunk/manager.c @@ -43,6 +43,9 @@ ChunkManager_init () chunk_manager.chunk_meshes = g_hash_table_new_full (cm_ivec2_hash, cm_ivec2_equal, free, (GDestroyNotify) Chunk_free); } +/* TODO: Update the chunks only if the player has moved into another + chunk, to save frame time. */ + void ChunkManager_update (vec3 camera_position) { diff --git a/game/src/entity/#templates.c# b/game/src/entity/#templates.c# new file mode 100644 index 0000000..c89e2db --- /dev/null +++ b/game/src/entity/#templates.c# @@ -0,0 +1,25 @@ +#include "templates.h" + +GHashTable *entity_templates; + +void +EntityTemplates_init () +{ + entity_templates = g_hash_table_new (g_str_hash, g_str_equal); + + Entity *tree = malloc (sizeof (Entity)); + *tree = (Entity) { + .type = ENTITY_SOLID, + .mesh_type = ENTITY_MESH_X, + .origin = ENTITY_BOTTOM, + .position = { 0.0, 0.0, 0.0 }, + .scale = { 1.0, 3.0, 1.0 }, + .rotation = 0.0, + .texture = "tree0" + }; + + ET_ENTADD ("tree", tree); +} + +/* To avoid confusion with EM_ENTADD. */ +#undef ET_ENTADD diff --git a/game/src/entity/.#templates.c b/game/src/entity/.#templates.c new file mode 120000 index 0000000..9eac588 --- /dev/null +++ b/game/src/entity/.#templates.c @@ -0,0 +1 @@ +emssej@Komputer-Ze-Smieci.2233:1748755544 \ No newline at end of file diff --git a/game/src/entity/entity.h b/game/src/entity/entity.h index 052ec58..f7ea570 100644 --- a/game/src/entity/entity.h +++ b/game/src/entity/entity.h @@ -1,18 +1,16 @@ #pragma once -typedef enum EntityMeshType - { - ENTITY_MESH_BILLBOARD, - ENTITY_MESH_X - } EntityMeshType; - typedef struct Entity { + enum { ENTITY_DEFAULT, ENTITY_SOLID } type; + enum { ENTITY_MESH_BILLBOARD, ENTITY_MESH_X } mesh_type; + enum { ENTITY_CENTER, ENTITY_BOTTOM } origin; + void *userdata; + + void (*on_tick) (struct Entity *self); + vec3 position; - vec3 rotation; /* Unused for now. */ + float rotation; vec3 scale; /* Unused for now. */ - int texture; - EntityMeshType type; + char *texture; } Entity; - -Entity *Entity_new (vec3 position, int texture, EntityMeshType type); diff --git a/game/src/entity/manager.c b/game/src/entity/manager.c index 4f29f81..a8986bb 100644 --- a/game/src/entity/manager.c +++ b/game/src/entity/manager.c @@ -1,9 +1,14 @@ #include "manager.h" -GSequence *entity_manager; +/* TODO: Sort entities by mesh type AS WELL AS distance. Binding + buffers is very performance-sensitive, thus to conserve speed one + type should be rendered in one go, then the other type, etc. */ +GSequence *entity_manager; EntityMesh mesh_billboard, mesh_x; +extern Resources resources; + void EntityManager_init () { @@ -12,14 +17,65 @@ EntityManager_init () mesh_x = EntityMesh_make_x (); } +void +em_tick_internal (gpointer data, gpointer userdata) +{ + UNUSED (userdata); + Entity *entity = data; + + if (entity->on_tick) + { + entity->on_tick (entity); + } +} + void EntityManager_tick () { + g_sequence_foreach (entity_manager, em_tick_internal, NULL); +} + +void +em_render_internal (gpointer data, gpointer userdata) +{ + UNUSED (userdata); + Entity *entity = data; + EntityMesh to_render; + unsigned int program; + switch (entity->mesh_type) + { + case ENTITY_MESH_X: + program = R_PRGGET ("default"); + to_render = mesh_x; + break; + default: + program = R_PRGGET ("billboard"); + to_render = mesh_billboard; + break; + } + + mat4 model = GLM_MAT4_IDENTITY_INIT; + glm_translate (model, entity->position); + + if (entity->origin == ENTITY_BOTTOM) + { + glm_translate (model, (vec3) { 0.0, entity->scale[1], 0.0 }); + } + + glm_scale (model, entity->scale); + + program_use (program); + program_set_mat4 (program, "model", model); + + glActiveTexture (GL_TEXTURE0); + glBindTexture (GL_TEXTURE_2D, R_TEXGET (entity->texture)); + + EntityMesh_render (&to_render); } void EntityManager_render () { - + g_sequence_foreach (entity_manager, em_render_internal, NULL); } diff --git a/game/src/entity/manager.h b/game/src/entity/manager.h index f7d6001..d522717 100644 --- a/game/src/entity/manager.h +++ b/game/src/entity/manager.h @@ -5,10 +5,10 @@ #include "entity.h" #include "meshes.h" +#include "../util.h" +#include "../resources.h" -/* TODO: Sort entities by mesh type AS WELL AS distance. Binding - buffers is very performance-sensitive, thus to conserve speed one - type should be rendered in one go, then the other type, etc. */ +#define EM_ENTADD(entity) (g_sequence_append (entity_manager, entity)); void EntityManager_init (); void EntityManager_tick (); diff --git a/game/src/entity/meshes.c b/game/src/entity/meshes.c index a4a70a9..b754295 100644 --- a/game/src/entity/meshes.c +++ b/game/src/entity/meshes.c @@ -1,11 +1,11 @@ #include "meshes.h" -/* TODO: add back faces. */ +/* TODO: Split binding/rendering */ extern GameFlags game_flags; void -EntityMesh_bind (EntityMesh *mesh) +EntityMesh_render (EntityMesh *mesh) { glBindBuffer (GL_ARRAY_BUFFER, mesh->pbo); glEnableVertexAttribArray (0); @@ -16,11 +16,7 @@ EntityMesh_bind (EntityMesh *mesh) glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, sizeof (float) * 2, 0); glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, mesh->ebo); -} -void -EntityMesh_render (EntityMesh *mesh) -{ if (game_flags.wireframe_enabled) { glDrawElements (GL_LINES, mesh->indices_len, GL_UNSIGNED_INT, 0); diff --git a/game/src/entity/meshes.h b/game/src/entity/meshes.h index 0bee0ce..e81867a 100644 --- a/game/src/entity/meshes.h +++ b/game/src/entity/meshes.h @@ -13,7 +13,6 @@ typedef struct EntityMesh unsigned int ebo; } EntityMesh; -void EntityMesh_bind (EntityMesh *mesh); void EntityMesh_render (EntityMesh *mesh); EntityMesh EntityMesh_make_billboard (); EntityMesh EntityMesh_make_x (); diff --git a/game/src/entity/templates.c b/game/src/entity/templates.c new file mode 100644 index 0000000..eb63b4c --- /dev/null +++ b/game/src/entity/templates.c @@ -0,0 +1,32 @@ +#include "templates.h" + +GHashTable *entity_templates; + +void +EntityTemplates_init () +{ + entity_templates = g_hash_table_new (g_str_hash, g_str_equal); + + Entity *tree = malloc (sizeof (Entity)); + *tree = (Entity) { + .type = ENTITY_SOLID, + .mesh_type = ENTITY_MESH_X, + .origin = ENTITY_BOTTOM, + .position = { 0.0, 0.0, 0.0 }, + .scale = { 1.0, 3.0, 1.0 }, + .rotation = 0.0, + .texture = "tree0" + }; + + ET_ENTADD ("tree", tree); +} + +/* Example function for a tick for an entity. */ +void +example_entity_tick (Entity *entity) +{ + entity->scale[1] += 0.0000001; +} + +/* To avoid confusion with EM_ENTADD. */ +#undef ET_ENTADD diff --git a/game/src/entity/templates.h b/game/src/entity/templates.h new file mode 100644 index 0000000..f7ae8ab --- /dev/null +++ b/game/src/entity/templates.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include +#include + +#include "entity.h" + +#define ET_ENTGET(key) ((Entity *) g_hash_table_lookup (entity_templates, key)) +#define ET_ENTADD(key, entity) (g_hash_table_insert (entity_templates, key, entity)) + +void EntityTemplates_init (); diff --git a/game/src/graphics.c b/game/src/graphics.c index ef3d8d6..866f98a 100644 --- a/game/src/graphics.c +++ b/game/src/graphics.c @@ -6,9 +6,6 @@ extern ChunkManager chunk_manager; extern GameState game_state; extern Resources resources; -extern EntityMesh mesh_billboard; -extern EntityMesh mesh_x; - void GraphicsContext_init () { @@ -60,32 +57,33 @@ GraphicsContext_render () mat4 projection, view; Camera_get_projection (&game_state.camera, 640.0 / 480.0, projection); Camera_get_view (&game_state.camera, view); - - program_use (_RGP ("chunk")); - program_set_mat4 (_RGP ("chunk"), "projection", projection); - program_set_mat4 (_RGP ("chunk"), "view", view); + /* Set views and projections in all programs. TODO: make this dynamic*/ + program_use (R_PRGGET ("billboard")); + program_set_mat4 (R_PRGGET ("billboard"), "projection", projection); + program_set_mat4 (R_PRGGET ("billboard"), "view", view); + + program_use (R_PRGGET ("default")); + program_set_mat4 (R_PRGGET ("default"), "projection", projection); + program_set_mat4 (R_PRGGET ("default"), "view", view); + + program_use (R_PRGGET ("chunk")); + program_set_mat4 (R_PRGGET ("chunk"), "projection", projection); + program_set_mat4 (R_PRGGET ("chunk"), "view", view); + glActiveTexture (GL_TEXTURE0); - glBindTexture (GL_TEXTURE_2D, _RGT ("terrain0")); + glBindTexture (GL_TEXTURE_2D, R_TEXGET ("terrain0")); glActiveTexture (GL_TEXTURE1); - glBindTexture (GL_TEXTURE_2D, _RGT ("terrain1")); + glBindTexture (GL_TEXTURE_2D, R_TEXGET ("terrain1")); glActiveTexture (GL_TEXTURE2); - glBindTexture (GL_TEXTURE_2D, _RGT ("terrain2")); + glBindTexture (GL_TEXTURE_2D, R_TEXGET ("terrain2")); glActiveTexture (GL_TEXTURE3); - glBindTexture (GL_TEXTURE_2D, _RGT ("terrain3")); + glBindTexture (GL_TEXTURE_2D, R_TEXGET ("terrain3")); glActiveTexture (GL_TEXTURE4); - glBindTexture (GL_TEXTURE_2D, _RGT ("terrain4")); + glBindTexture (GL_TEXTURE_2D, R_TEXGET ("terrain4")); - ChunkManager_render (game_state.camera.position, _RGP ("chunk")); - - program_use (_RGP ("default")); - program_set_mat4 (_RGP ("default"), "projection", projection); - program_set_mat4 (_RGP ("default"), "view", view); - glActiveTexture (GL_TEXTURE0); - glBindTexture (GL_TEXTURE_2D, _RGT ("tree0")); - - EntityMesh_bind (&mesh_x); - EntityMesh_render (&mesh_x); + ChunkManager_render (game_state.camera.position, R_PRGGET ("chunk")); + EntityManager_render (); SDL_GL_SwapWindow (gfx.window); } diff --git a/game/src/main.c b/game/src/main.c index 6ff4790..ef5f7ea 100644 --- a/game/src/main.c +++ b/game/src/main.c @@ -6,6 +6,7 @@ #include "event/manager.h" #include "event/handlers.h" #include "chunk/manager.h" +#include "entity/templates.h" #include "state.h" #include "resources.h" #include "graphics.h" @@ -24,6 +25,7 @@ SDL_AppInit (void **appstate, int argc, char **argv) Resources_init (); ChunkManager_init (); EntityManager_init (); + EntityTemplates_init (); GameState_init (SDL_GetTicks ()); /* Set some basic game flags for testing. */ game_flags.can_free_look = 1; @@ -51,8 +53,9 @@ SDL_AppIterate (void *appstate) { UNUSED (appstate); - ChunkManager_update (game_state.camera.position); GameState_tick (SDL_GetTicks ()); + EntityManager_tick (); + ChunkManager_update (game_state.camera.position); GraphicsContext_render (); return SDL_APP_CONTINUE; diff --git a/game/src/resources.c b/game/src/resources.c index 86c68b9..fb2c970 100644 --- a/game/src/resources.c +++ b/game/src/resources.c @@ -8,20 +8,20 @@ Resources_init () resources.programs = g_hash_table_new (g_str_hash, g_str_equal); resources.textures = g_hash_table_new (g_str_hash, g_str_equal); - _RAP ("default", _RLP ("res/shaders/default.vert", "res/shaders/default.frag")); - _RAP ("billboard", _RLP ("res/shaders/billboard.vert", "res/shaders/billboard.frag")); - _RAP ("chunk", _RLP ("res/shaders/chunk.vert", "res/shaders/chunk.frag")); + R_PRGADD ("default", R_PRGLOAD ("res/shaders/default.vert", "res/shaders/default.frag")); + R_PRGADD ("billboard", R_PRGLOAD ("res/shaders/billboard.vert", "res/shaders/billboard.frag")); + R_PRGADD ("chunk", R_PRGLOAD ("res/shaders/chunk.vert", "res/shaders/chunk.frag")); - program_use (_RGP ("chunk")); - program_set_int_array (_RGP ("chunk"), "materials", 5, (int[5]) { 0, 1, 2, 3, 4 }); + program_use (R_PRGGET ("chunk")); + program_set_int_array (R_PRGGET ("chunk"), "materials", 5, (int[5]) { 0, 1, 2, 3, 4 }); - _RAT ("terrain0", _RLT ("res/textures/dirt.png")); - _RAT ("terrain1", _RLT ("res/textures/drygrass.png")); - _RAT ("terrain2", _RLT ("res/textures/grass.png")); - _RAT ("terrain3", _RLT ("res/textures/rock_cement.png")); - _RAT ("terrain4", _RLT ("res/textures/asphalt_gravel.png")); + R_TEXADD ("terrain0", R_TEXLOAD ("res/textures/dirt.png")); + R_TEXADD ("terrain1", R_TEXLOAD ("res/textures/drygrass.png")); + R_TEXADD ("terrain2", R_TEXLOAD ("res/textures/grass.png")); + R_TEXADD ("terrain3", R_TEXLOAD ("res/textures/rock_cement.png")); + R_TEXADD ("terrain4", R_TEXLOAD ("res/textures/asphalt_gravel.png")); - _RAT ("tree0", _RLT ("res/textures/tree.png")); + R_TEXADD ("tree0", R_TEXLOAD ("res/textures/tree.png")); } unsigned int diff --git a/game/src/resources.h b/game/src/resources.h index bbac559..3ef6d6c 100644 --- a/game/src/resources.h +++ b/game/src/resources.h @@ -6,12 +6,12 @@ #include "program.h" #include "texture.h" -#define _RAP(key, program) (g_hash_table_insert (resources.programs, key, GINT_TO_POINTER (program))) -#define _RAT(key, texture) (g_hash_table_insert (resources.textures, key, GINT_TO_POINTER (texture))) -#define _RLP(v_path, f_path) (Resources_load_program (v_path, f_path)) -#define _RLT(path) (Resources_load_texture (path)) -#define _RGP(key) (GPOINTER_TO_INT (g_hash_table_lookup (resources.programs, key))) -#define _RGT(key) (GPOINTER_TO_INT (g_hash_table_lookup (resources.textures, key))) +#define R_PRGADD(key, program) (g_hash_table_insert (resources.programs, key, GINT_TO_POINTER (program))) +#define R_TEXADD(key, texture) (g_hash_table_insert (resources.textures, key, GINT_TO_POINTER (texture))) +#define R_PRGLOAD(v_path, f_path) (Resources_load_program (v_path, f_path)) +#define R_TEXLOAD(path) (Resources_load_texture (path)) +#define R_PRGGET(key) (GPOINTER_TO_INT (g_hash_table_lookup (resources.programs, key))) +#define R_TEXGET(key) (GPOINTER_TO_INT (g_hash_table_lookup (resources.textures, key))) typedef struct Resources { GHashTable *programs;