diff --git a/game/Makefile b/game/Makefile index b4d592c..4a9893c 100644 --- a/game/Makefile +++ b/game/Makefile @@ -5,8 +5,8 @@ SRC=src/main.c src/gl/program.c src/resources.c src/camera.c src/chunk/chunk.c \ src/ui/uiman.c OBJ=$(SRC:.c=.o) -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` +CFLAGS=-g -Werror -Wextra -Wall `pkg-config --cflags sdl3 sdl3-image cglm gl glib-2.0` +LDFLAGS=-g -lm `pkg-config --libs sdl3 sdl3-image cglm gl glib-2.0` TARGET=wschod CC=gcc LD=gcc @@ -22,4 +22,4 @@ $(TARGET): $(OBJ) $(CC) -c $^ -o $@ $(CFLAGS) clean: - $(RM) gmon.out $(TARGET) $(OBJ) + $(RM) perf.data* $(TARGET) $(OBJ) diff --git a/game/cputest.sh b/game/cputest.sh index 0eb86ac..659c6e1 100755 --- a/game/cputest.sh +++ b/game/cputest.sh @@ -1,2 +1,2 @@ #!/usr/bin/env bash -cpulimit -l 20 -- ./wschod +cpulimit -l $1 -- ./wschod diff --git a/game/res/textures/font.png b/game/res/textures/font.png index 538cdf5..6f41349 100644 Binary files a/game/res/textures/font.png and b/game/res/textures/font.png differ diff --git a/game/res/textures/font.xcf b/game/res/textures/font.xcf index 32430c6..675334b 100644 Binary files a/game/res/textures/font.xcf and b/game/res/textures/font.xcf differ diff --git a/game/shell.nix b/game/shell.nix index cb16345..a9dd998 100644 --- a/game/shell.nix +++ b/game/shell.nix @@ -16,5 +16,7 @@ pkgs.mkShellNoCC { pkg-config valgrind gdb + cpulimit + linuxKernel.packages.linux_6_12.perf ]; } diff --git a/game/src/chunk/chunkman.c b/game/src/chunk/chunkman.c index 20e8de6..a67fe09 100644 --- a/game/src/chunk/chunkman.c +++ b/game/src/chunk/chunkman.c @@ -1,8 +1,5 @@ #include "chunkman.h" -/* TODO: perhaps render the chunks in a circle, not a square, around - the player? */ - int n_frame_chunks; static GHashTable *chunks; @@ -24,17 +21,14 @@ chunkman_ivec2_hash (gconstpointer key) gboolean chunkman_ivec2_equal (gconstpointer a, gconstpointer b) { - /* Much faster than comparing the integer values. */ return memcmp (a, b, sizeof (int) * 2) == 0; } ivec2 * chunkman_make_key(int x, int z) { - ivec2 *key = malloc(sizeof(ivec2)); - (*key)[0] = x; - (*key)[1] = z; - return key; + ivec2 key = { x, z }; + return g_memdup2 (&key, sizeof (ivec2)); } void @@ -107,43 +101,63 @@ chunkman_init () chunk_meshes = g_hash_table_new_full (chunkman_ivec2_hash, chunkman_ivec2_equal, free, (GDestroyNotify) Chunk_free); } -/* TODO: Update the chunks only if the player has moved into another - chunk, to save frame time. */ - void -chunkman_update (vec3 position) +chunkman_tick (vec3 position) { int cx, cz; chunkman_world_to_chunk (position[0], position[2], &cx, &cz); + unsigned int frame_time = SDL_GetTicks (); + for (int z = cz - RENDER_DISTANCE - 1; z <= cz + RENDER_DISTANCE + 1; ++z) { for (int x = cx - RENDER_DISTANCE - 1; x <= cx + RENDER_DISTANCE + 1; ++x) - { - if (chunkman_get_Chunk_at (x, z) == NULL) - { - chunkman_generate_Chunk_at (x, z); - } - } + { + if ((x - cx) * (x - cx) + (z - cz) * (z - cz) <= (RENDER_DISTANCE + 1) * (RENDER_DISTANCE + 1)) + { + if (chunkman_get_Chunk_at (x, z) == NULL) + { + chunkman_generate_Chunk_at (x, z); + } + + if (SDL_GetTicks () - frame_time > FRAME_BUDGET) + { + return; + } + } + } } for (int z = cz - RENDER_DISTANCE; z <= cz + RENDER_DISTANCE; ++z) { for (int x = cx - RENDER_DISTANCE; x <= cx + RENDER_DISTANCE; ++x) - { - if (chunkman_get_ChunkMesh_at (x, z) == NULL) - { - chunkman_generate_ChunkMesh_at (x, z); - } - } + { + if ((x - cx) * (x - cx) + (z - cz) * (z - cz) <= RENDER_SQR) + { + if (chunkman_get_ChunkMesh_at (x, z) == NULL) + { + chunkman_generate_ChunkMesh_at (x, z); + } + + if (SDL_GetTicks () - frame_time > FRAME_BUDGET) + { + return; + } + } + } } } void -chunkman_render (vec3 position) +chunkman_render (vec3 position, vec4 planes[6]) { +#if !FRUSTUM_CULL + UNUSED (planes); +#endif + int cx = 0, cz = 0; chunkman_world_to_chunk (position[0], position[2], &cx, &cz); + program_use (RESPRG ("chunk")); glActiveTexture (GL_TEXTURE0); @@ -163,18 +177,35 @@ chunkman_render (vec3 position) { for (int x = cx - RENDER_DISTANCE; x <= cx + RENDER_DISTANCE; ++x) { - ChunkMesh *mesh = chunkman_get_ChunkMesh_at (x, z); - if (mesh != NULL) + if ((x - cx) * (x - cx) + (z - cz) * (z - cz) <= RENDER_SQR) { - if (!bound) + ChunkMesh *mesh = chunkman_get_ChunkMesh_at (x, z); + if (mesh != NULL) { - ChunkMesh_bind (mesh); - bound = true; - } + /* This, compared to the entity AABB, seems to work + completely fine. */ +#if FRUSTUM_CULL + vec3 aabb[2] = { + { x * CHUNK_DIM, 0.0, z * CHUNK_DIM }, + { (x + 1) * CHUNK_DIM, TERRAIN_HEIGHT, (z + 1) * CHUNK_DIM } + }; + + if (!glm_aabb_frustum (aabb, planes)) + { + continue; + } +#endif + + if (!bound) + { + ChunkMesh_bind (mesh); + bound = true; + } - program_set_vec3_ind (RESPRG ("chunk"), "chunk_position", x * CHUNK_DIM, 0.0, z * CHUNK_DIM); - ChunkMesh_render (mesh); - n_frame_chunks++; + program_set_vec3_ind (RESPRG ("chunk"), "chunk_position", x * CHUNK_DIM, 0.0, z * CHUNK_DIM); + ChunkMesh_render (mesh); + n_frame_chunks++; + } } } } @@ -185,7 +216,8 @@ chunkman_render (vec3 position) Chunk * chunkman_get_Chunk_at (int x, int z) { - return (Chunk *) g_hash_table_lookup (chunks, (ivec2) { x, z }); + ivec2 key = { x, z }; + return (Chunk *) g_hash_table_lookup (chunks, &key); } void @@ -205,12 +237,13 @@ chunkman_generate_Chunk_at (int cx, int cz) { for (int x = 0; x < CHUNK_DIM; ++x) { + chunk->heightmap[INDEX (x, z)] = (glm_perlin_vec2 - ((vec2) { (x + xoffset + 0.5) / 10.0, (z + zoffset + 0.5) / 10.0 }) + 0.6) * 2; + ((vec2) { (x + xoffset + 0.333) / 10.0, (z + zoffset + 0.333) / 10.0 }) + 0.6) * 2; 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; + (glm_perlin_vec2 ((vec2) { (x + xoffset + 0.333) / 20.0, (z + zoffset + 0.333) / 20.0 }) + 0.6 * 5) % 5; if (rand() % 100 < 2) { @@ -250,7 +283,8 @@ chunkman_get_Chunk_neighbors_at (int x, int z, Chunk **nr, Chunk **nd, Chunk **n ChunkMesh * chunkman_get_ChunkMesh_at (int x, int z) { - return (ChunkMesh *) g_hash_table_lookup (chunk_meshes, (ivec2) { x, z }); + ivec2 key = { x, z }; + return (ChunkMesh *) g_hash_table_lookup (chunk_meshes, key); } void diff --git a/game/src/chunk/chunkman.h b/game/src/chunk/chunkman.h index 1fcb6c3..491ef16 100644 --- a/game/src/chunk/chunkman.h +++ b/game/src/chunk/chunkman.h @@ -8,12 +8,16 @@ #include "../gl/program.h" #include "../entity/entman.h" #include "../entity/templates.h" +#include "../graphics.h" -#define RENDER_DISTANCE 3 +#define RENDER_DISTANCE 5 +#define RENDER_SQR (RENDER_DISTANCE*RENDER_DISTANCE) +#define TERRAIN_HEIGHT 8.0 +#define FRAME_BUDGET 3 void chunkman_init (); -void chunkman_update (vec3 position); -void chunkman_render (vec3 position); +void chunkman_tick (vec3 position); +void chunkman_render (vec3 position, vec4 planes[6]); float chunkman_get_height_at (float x, float z); diff --git a/game/src/entity/entity.h b/game/src/entity/entity.h index f77e5b9..b9f7d8e 100644 --- a/game/src/entity/entity.h +++ b/game/src/entity/entity.h @@ -12,6 +12,9 @@ typedef struct Entity enum { RENDER_NORMAL, RENDER_BILLBOARD_Y, RENDER_BILLBOARD } render_type; bool visible; bool dont_cull; + float cull_distance; /* 1 means as far as the terrain goes, + less than 1, for example 0.5, means + half as far as the terrain goes. */ bool wavy; void *userdata; diff --git a/game/src/entity/entman.c b/game/src/entity/entman.c index 7b94851..301caec 100644 --- a/game/src/entity/entman.c +++ b/game/src/entity/entman.c @@ -1,6 +1,8 @@ #include "entman.h" -/* TODO: Put uniforms into vertex attributes for pseudoinstancing. */ +/* TODO: What COULD be done, but not necessarily SHOULD be done, is to + generate a mesh for say, 16 entities at once, then, the number of + drawcalls would drop substantially. */ /* TODO: Instead of having a huge far plane, render the clouds seperately somehow. */ @@ -9,7 +11,9 @@ extern vec3 camera_position; Entity *player_entity, *sky_entity; int n_frame_ents; -static GSequence *entities; +/* The GArray is the fastest of all sequence types. Should work fine + for a billion entities. */ +static GArray *entities; static SimpleMesh mesh_quad1, mesh_quad2, mesh_quad_x; static unsigned int last_program = -1; static unsigned int last_type = -1; @@ -24,7 +28,7 @@ character_on_tick (Entity *entity) void entman_init () { - entities = g_sequence_new (NULL); + entities = g_array_new (false, false, sizeof (Entity *)); mesh_quad1 = SimpleMesh_make_quad1 (); mesh_quad2 = SimpleMesh_make_quad2 (); mesh_quad_x = SimpleMesh_make_quad_x (); @@ -57,28 +61,22 @@ entman_init () } void -entman_tick_internal (gpointer data, gpointer userdata) +entman_tick () { - UNUSED (userdata); - Entity *entity = data; - - if (entity->on_tick) + for (guint i = 0; i < entities->len; ++i) { - entity->on_tick (entity); + Entity *e = g_array_index (entities, Entity*, i); + + if (e->on_tick) + { + e->on_tick (e); + } } } void -entman_tick () +entman_render_entity (Entity *entity, void *userdata) { - g_sequence_foreach (entities, entman_tick_internal, NULL); -} - -void -entman_render_internal (gpointer data, gpointer userdata) -{ - Entity *entity = data; - struct { mat4 *view; #ifdef FRUSTUM_CULL @@ -91,8 +89,8 @@ entman_render_internal (gpointer data, gpointer userdata) return; } - if (glm_vec3_distance (camera_position, entity->position) > ENTITY_RENDER_DISTANCE - && !entity->dont_cull) + if (glm_vec3_distance (camera_position, entity->position) + > entity->cull_distance * RENDER_DISTANCE * CHUNK_DIM && !entity->dont_cull) { return; } @@ -203,36 +201,41 @@ entman_render_internal (gpointer data, gpointer userdata) } void -entman_render (mat4 *view, mat4 *proj) +entman_render (mat4 *view, vec4 planes[6]) { +#if !FRUSTUM_CULL + UNUSED (planes); +#endif + last_program = -1; last_texture = -1; last_type = -1; n_frame_ents = 0; - -#ifdef FRUSTUM_CULL - vec4 planes[6]; - mat4 view_proj; - glm_mat4_mul (*proj, *view, view_proj); - glm_frustum_planes (view_proj, planes); -#endif struct { mat4 *view; -#ifdef FRUSTUM_CULL +#if FRUSTUM_CULL vec4 *frustum_planes; #endif - } data = { view, planes }; - - g_sequence_foreach (entities, entman_render_internal, &data); + } data = { + view +#if FRUSTUM_CULL + ,planes +#endif + }; + + for (guint i = 0; i < entities->len; ++i) + { + Entity *e = g_array_index (entities, Entity*, i); + entman_render_entity (e, &data); + } } static inline int -entman_compare (gconstpointer a, gconstpointer b, gpointer userdata) +entman_compare (gconstpointer a, gconstpointer b) /* This function is used to sort Entities by their mesh type, so that diferrent buffers aren't bound every single frame. */ { - UNUSED (userdata); const Entity *entity1 = a; const Entity *entity2 = b; @@ -242,5 +245,5 @@ entman_compare (gconstpointer a, gconstpointer b, gpointer userdata) void entman_add_entity (Entity *entity) { - g_sequence_insert_sorted (entities, entity, entman_compare, NULL); + g_array_append_val (entities, entity); } diff --git a/game/src/entity/entman.h b/game/src/entity/entman.h index 2bd1d98..d04e9c7 100644 --- a/game/src/entity/entman.h +++ b/game/src/entity/entman.h @@ -9,9 +9,7 @@ #include "../resources.h" #include "../state.h" #include "../chunk/chunkman.h" - -#define ENTITY_RENDER_DISTANCE (RENDER_DISTANCE*CHUNK_DIM) -#define FRUSTUM_CULL 1 +#include "../graphics.h" #define ENTADD(entity) (entman_add_entity (entity)) diff --git a/game/src/entity/templates.c b/game/src/entity/templates.c index ceec908..c23fc15 100644 --- a/game/src/entity/templates.c +++ b/game/src/entity/templates.c @@ -23,6 +23,7 @@ ent_templates_init () *tree = (Entity) { .mesh_type = MESH_QUAD_X, .visible = true, + .cull_distance = 0.7, .wavy = true, .origin = ORIGIN_BOTTOM, .position = { 0.0 }, @@ -39,6 +40,7 @@ ent_templates_init () .mesh_type = MESH_QUAD1, .render_type = RENDER_BILLBOARD_Y, .visible = true, + .cull_distance = 0.2, .wavy = true, .origin = ORIGIN_BOTTOM, .position = { 0.0 }, diff --git a/game/src/graphics.c b/game/src/graphics.c index 1a06827..a1eedd3 100644 --- a/game/src/graphics.c +++ b/game/src/graphics.c @@ -69,8 +69,15 @@ graphics_render () resources_programs_set_mat4 ("view", &view); resources_programs_set_float ("time", last_time); - chunkman_render (camera_position); - entman_render (view, projection); + vec4 planes[6]; +#if FRUSTUM_CULL + mat4 view_proj; + glm_mat4_mul (projection, view, view_proj); + glm_frustum_planes (view_proj, planes); +#endif + + chunkman_render (camera_position, planes); + entman_render (view, planes); /* We don't care about blending elsewhere for now. */ glEnable (GL_BLEND); diff --git a/game/src/graphics.h b/game/src/graphics.h index 40c61b2..a7443c1 100644 --- a/game/src/graphics.h +++ b/game/src/graphics.h @@ -10,6 +10,8 @@ #include "resources.h" #include "util.h" +#define FRUSTUM_CULL 1 + void graphics_init (); void graphics_render (); void graphics_free (); diff --git a/game/src/main.c b/game/src/main.c index e4aecfb..bef1ab0 100644 --- a/game/src/main.c +++ b/game/src/main.c @@ -54,7 +54,7 @@ SDL_AppIterate (void *appstate) game_state_tick (SDL_GetTicks ()); entman_tick (); - chunkman_update (camera_position); + chunkman_tick (camera_position); graphics_render (); return SDL_APP_CONTINUE; diff --git a/game/src/state.c b/game/src/state.c index dc49185..f1f0437 100644 --- a/game/src/state.c +++ b/game/src/state.c @@ -3,7 +3,7 @@ extern vec3 camera_position; extern vec3 camera_rotation; extern Entity *player_entity, *sky_entity; -extern UIElement *player_pos, *n_ents, *n_chunks; +extern UIElement *player_pos, *n_ents, *n_chunks, *frame_time; extern int n_frame_ents, n_frame_chunks; float delta_time; @@ -33,16 +33,12 @@ game_state_tick (int ticks) sky_entity->position[0] = camera_position[0]; sky_entity->position[2] = camera_position[2]; - /* Put the player position into the UI. */ - player_pos->text = malloc (sizeof (char) * 32); - snprintf (player_pos->text, 32, "position X:%2.1f Y:%2.1f Z:%2.1f", player_entity->position[0], - player_entity->position[1], player_entity->position[2]); - /* Put the amount of entities rendered into the UI. */ - n_ents->text = malloc (sizeof (char) * 32); - snprintf (n_ents->text, 32, "entities rendered: %d", n_frame_ents); - /* Put the amount of chunks rendered into the UI. */ - n_chunks->text = malloc (sizeof (char) * 32); - snprintf (n_chunks->text, 32, "chunks rendered: %d", n_frame_chunks); + g_string_printf (player_pos->text, "Position (X:%2.1f, Y:%2.1f, Z:%2.1f)", + player_entity->position[0], player_entity->position[1], + player_entity->position[2]); + g_string_printf (n_ents->text, "Entities rendered: %d", n_frame_ents); + g_string_printf (n_chunks->text, "Chunks rendered: %d", n_frame_chunks); + g_string_printf (frame_time->text, "Frame time: %.3f", delta_time); if (flags_get ("moving_forward")) { diff --git a/game/src/ui/uielement.c b/game/src/ui/uielement.c index 18ce98b..24f4623 100644 --- a/game/src/ui/uielement.c +++ b/game/src/ui/uielement.c @@ -45,8 +45,6 @@ UIElement_render (UIElement *element) glm_scale (model, element->scale); program_set_mat4 (RESPRG ("uielement"), "model", model); - /* TODO: rename this function to vec3_individual or - something. */ program_set_vec3 (RESPRG ("uielement"), "color", element->background_color); SimpleMesh_render (&quad); @@ -56,7 +54,7 @@ UIElement_render (UIElement *element) glBindTexture (GL_TEXTURE_2D, RESTEX ("font0")); program_set_vec3 (RESPRG ("uielement"), "color", element->text_color); - for (size_t i = 0; i < strlen (element->text); ++i) + for (size_t i = 0; i < element->text->len; ++i) { vec3 pos; float x = -element->scale[0] + (element->scale[1] / 2) + (element->scale[1] * i); @@ -67,7 +65,7 @@ UIElement_render (UIElement *element) glm_scale (model, (vec3) { element->scale[1] / 2, element->scale[1], 1.0 }); program_set_mat4 (RESPRG ("uielement"), "model", model); - program_set_float (RESPRG ("uielement"), "character", element->text[i]); + program_set_float (RESPRG ("uielement"), "character", element->text->str[i]); SimpleMesh_render (&quad); } } diff --git a/game/src/ui/uielement.h b/game/src/ui/uielement.h index 9d20106..6f8efcf 100644 --- a/game/src/ui/uielement.h +++ b/game/src/ui/uielement.h @@ -26,7 +26,7 @@ typedef struct UIElement vec3 position; vec3 scale; - char *text; /* Maybe switch to GString? */ + GString *text; } UIElement; void ui_element_mesh_init (); diff --git a/game/src/ui/uiman.c b/game/src/ui/uiman.c index ea839d3..e64e2a0 100644 --- a/game/src/ui/uiman.c +++ b/game/src/ui/uiman.c @@ -1,11 +1,11 @@ #include "uiman.h" -UIElement *player_pos, *n_ents, *n_chunks; +UIElement *player_pos, *n_ents, *n_chunks, *frame_time; static UIElement ui_root; #define UINEWROOT() ((UIElement) { .parent = NULL, .children = g_sequence_new (NULL), .visible = false }) -#define UINEW(pare) ((UIElement) { .parent = pare, .text_color = { 1.0, 1.0, 1.0 }, .visible = true }) +#define UINEW(pare) ((UIElement) { .parent = pare, .text_color = { 1.0, 1.0, 1.0 }, .visible = true, .text = g_string_new (NULL) }) #define UIADD(pare, child) (UIElement_add_child (pare, child)) void @@ -32,6 +32,19 @@ uiman_init () glm_vec3_copy ((vec3) { -0.65, 0.70, 0.0 }, n_chunks->position); glm_vec3_copy ((vec3) { 0.3, 0.03, 1.0 }, n_chunks->scale); UIADD (&ui_root, n_chunks); + + frame_time = malloc (sizeof (UIElement)); + *frame_time = UINEW (&ui_root); + glm_vec3_copy ((vec3) { -0.65, 0.59, 0.0 }, frame_time->position); + glm_vec3_copy ((vec3) { 0.3, 0.03, 1.0 }, frame_time->scale); + UIADD (&ui_root, frame_time); + +} + +void +uiman_tick () +{ + } void