From 09c142e923e02111d7be2f78ab2bfe51c69b97fd Mon Sep 17 00:00:00 2001 From: Emil Kosz Date: Wed, 23 Jul 2025 18:48:46 +0200 Subject: [PATCH] Flatten chunk and entity managers --- game/src/chunk/chunkman.c | 304 --------------------------------- game/src/chunk/chunkman.h | 31 ---- game/src/entity/entity.c | 114 +++++++++++++ game/src/entity/entity.h | 22 ++- game/src/entity/entman.c | 269 ----------------------------- game/src/entity/entman.h | 33 ---- game/src/gl/mesh_globals.c | 15 ++ game/src/gl/mesh_globals.h | 5 + game/src/graphics.h | 2 - game/src/main.c | 9 +- game/src/stage/stage_common.c | 19 ++- game/src/stage/stage_common.h | 7 +- game/src/stage/stage_ingame.c | 30 +++- game/src/stage/stage_ingame.h | 2 - game/src/ui/widget_render.c | 16 +- game/src/ui/widget_render.h | 2 +- game/src/world.c | 307 ++++++++++++++++++++++++++++++++++ game/src/world.h | 42 +++++ 18 files changed, 543 insertions(+), 686 deletions(-) delete mode 100644 game/src/chunk/chunkman.c delete mode 100644 game/src/chunk/chunkman.h delete mode 100644 game/src/entity/entman.c delete mode 100644 game/src/entity/entman.h create mode 100644 game/src/gl/mesh_globals.c create mode 100644 game/src/gl/mesh_globals.h create mode 100644 game/src/world.c create mode 100644 game/src/world.h diff --git a/game/src/chunk/chunkman.c b/game/src/chunk/chunkman.c deleted file mode 100644 index 5d736a2..0000000 --- a/game/src/chunk/chunkman.c +++ /dev/null @@ -1,304 +0,0 @@ -#include "chunkman.h" - -static GHashTable *chunks; -static GHashTable *chunk_meshes; - -static gboolean bound = false; - -static Profiler profiler; -static const int frame_budget_us = 500; - -guint -chunkman_ivec2_hash (gconstpointer key) -{ - /* This makes sure there's no collisions, thus making the whole - table much, much faster. */ - const ivec2 *v = key; - guint x = (guint) (*v)[0]; - guint y = (guint) (*v)[1]; - return (x * 73856093u) ^ (y * 19349663u); -} - -gboolean -chunkman_ivec2_equal (gconstpointer a, gconstpointer b) -{ - return memcmp (a, b, sizeof (int) * 2) == 0; -} - -ivec2 * -chunkman_make_key(int x, int z) -{ - ivec2 key = { x, z }; - return g_memdup2 (&key, sizeof (ivec2)); -} - -void -chunkman_world_to_chunk (float x, float z, int *cx, int *cz) -{ - *cx = floor (x / (float) CHUNK_DIM); - *cz = floor (z / (float) CHUNK_DIM); -} - -static inline float -c_get_height (Chunk *c, int x, int z) -{ - return c ? c->heightmap[INDEX (x, z)] : 0.0; -} - -float -chunkman_get_height_at (float x, float z) -{ - int cx, cz; - chunkman_world_to_chunk (x, z, &cx, &cz); - Chunk *chunk = chunkman_get_Chunk_at (cx, cz); - - if (chunk == NULL) - { - return 0.0; - } - - float lx = x - cx * CHUNK_DIM; - float lz = z - cz * CHUNK_DIM; - - int x0 = (int) floor (lx); - int z0 = (int) floor (lz); - int x1 = x0 + 1; - int z1 = z0 + 1; - - float tx = lx - x0; - float tz = lz - z0; - - Chunk *r = NULL, *d = NULL, *c = NULL; - chunkman_get_Chunk_neighbors_at (cx, cz, &r, &d, &c); - - float h00 = c_get_height (chunk, x0, z0); - float h10 = (x1 < CHUNK_DIM) ? c_get_height (chunk, x1, z0) : - (r ? c_get_height (r, 0, z0) : h00); - float h01 = (z1 < CHUNK_DIM) ? c_get_height (chunk, x0, z1) : - (d ? c_get_height (d, x0, 0) : h00); - float h11; - - if (x1 < CHUNK_DIM && z1 < CHUNK_DIM) - h11 = c_get_height (chunk, x1, z1); - else if (x1 >= CHUNK_DIM && z1 < CHUNK_DIM) - h11 = r ? c_get_height (r, 0, z1) : h10; - else if (x1 < CHUNK_DIM && z1 >= CHUNK_DIM) - h11 = d ? c_get_height (d, x1, 0) : h01; - else - h11 = c ? c_get_height (c, 0, 0) : h00; - - // Bilinear interpolation. - float h0 = h00 * (1 - tx) + h10 * tx; - float h1 = h01 * (1 - tx) + h11 * tx; - float height = h0 * (1 - tz) + h1 * tz; - - return height; -} - -void -chunkman_init () -{ - chunks = g_hash_table_new_full (chunkman_ivec2_hash, chunkman_ivec2_equal, free, (GDestroyNotify) Chunk_free); - chunk_meshes = g_hash_table_new_full (chunkman_ivec2_hash, chunkman_ivec2_equal, free, (GDestroyNotify) Chunk_free); -} - -void -chunkman_tick (vec3 position) -{ - int cx, cz; - chunkman_world_to_chunk (position[0], position[2], &cx, &cz); - - Profiler_begin (&profiler); - - int render_distance = gvar_get ("render_distance"); - - 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) - { - double elapsed = Profiler_sample (&profiler); - - if (elapsed > frame_budget_us) - { - return; - } - - if (chunkman_get_Chunk_at (x, z) == NULL) - { - chunkman_generate_Chunk_at (x, z); - } - } - } - - for (int z = cz - render_distance; z <= cz + render_distance; ++z) - { - for (int x = cx - render_distance; x <= cx + render_distance; ++x) - { - double elapsed = Profiler_sample (&profiler); - - if (elapsed > frame_budget_us) - { - return; - } - - if (chunkman_get_ChunkMesh_at (x, z) == NULL) - { - chunkman_generate_ChunkMesh_at (x, z); - } - } - } -} - -void -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 (PMPRG ("chunk")); - - texture_use (PMTEX ("terrain0"), 0); - texture_use (PMTEX ("terrain1"), 1); - texture_use (PMTEX ("terrain2"), 2); - texture_use (PMTEX ("terrain3"), 3); - texture_use (PMTEX ("terrain4"), 4); - - int render_distance = gvar_get ("render_distance"); - int frame_chunks = 0; - - for (int z = cz - render_distance; z <= cz + render_distance; ++z) - { - for (int x = cx - render_distance; x <= cx + render_distance; ++x) - { - /* Render in a circle instead of a square. */ - if ((x - cx) * (x - cx) + (z - cz) * (z - cz) > render_distance*render_distance) - { - continue; - } - - ChunkMesh *mesh = chunkman_get_ChunkMesh_at (x, z); - if (mesh != NULL) - { -#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 (PMPRG ("chunk"), "chunk_position", x * CHUNK_DIM, 0.0, z * CHUNK_DIM); - ChunkMesh_render (mesh, FLAG_GET ("wireframe_enabled")); - frame_chunks++; - } - } - } - - bound = false; - gvar_set ("frame_chunks", frame_chunks); -} - -Chunk * -chunkman_get_Chunk_at (int x, int z) -{ - ivec2 key = { x, z }; - return (Chunk *) g_hash_table_lookup (chunks, &key); -} - -void -chunkman_set_Chunk_at (int x, int z, Chunk *chunk) -{ - g_hash_table_insert (chunks, chunkman_make_key (x, z), chunk); -} - -void -chunkman_generate_Chunk_at (int cx, int cz) -{ - Chunk *chunk = Chunk_new (); - int xoffset = cx * CHUNK_DIM; - int zoffset = cz * CHUNK_DIM; - - for (int z = 0; z < CHUNK_DIM; ++z) - { - for (int x = 0; x < CHUNK_DIM; ++x) - { - - chunk->heightmap[INDEX (x, z)] = - (glm_perlin_vec2 - ((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.333) / 20.0, (z + zoffset + 0.333) / 20.0 }) + 0.6 * 5) % 5; - - if (rand() % 100 < 2) - { - entman_add_spawn_request - ("tree", - (vec3) { x + xoffset, chunk->heightmap[INDEX (x, z)], z + zoffset }, - (vec3) { 0.0, rand () % 180, 0.0 }, - (vec3) { 3.0, (rand () % 5) + 5, 3.0 }); - } - - if (rand() % 100 < 10) - { - entman_add_spawn_request - ("grass", - (vec3) { x + xoffset, chunk->heightmap[INDEX (x, z)], z + zoffset }, - (vec3) { 0.0, 0.0, 0.0 }, - (vec3) { 1.0, 1.0, 1.0 }); - } - } - } - - chunkman_set_Chunk_at (cx, cz, chunk); -} - -void -chunkman_get_Chunk_neighbors_at (int x, int z, Chunk **nr, Chunk **nd, Chunk **nc) -{ - *nr = chunkman_get_Chunk_at (x + 1, z); - *nd = chunkman_get_Chunk_at (x, z + 1); - *nc = chunkman_get_Chunk_at (x + 1, z + 1); -} - -ChunkMesh * -chunkman_get_ChunkMesh_at (int x, int z) -{ - ivec2 key = { x, z }; - return (ChunkMesh *) g_hash_table_lookup (chunk_meshes, key); -} - -void -chunkman_set_ChunkMesh_at (int x, int z, ChunkMesh *mesh) -{ - g_hash_table_insert (chunk_meshes, chunkman_make_key (x, z), mesh); -} - -void -chunkman_generate_ChunkMesh_at (int x, int z) -{ - Chunk *nr, *nd, *nc; - chunkman_get_Chunk_neighbors_at (x, z, &nr, &nd, &nc); - - g_assert (nr != NULL); - g_assert (nd != NULL); - g_assert (nc != NULL); - - chunkman_set_ChunkMesh_at - (x, z, ChunkMesh_build (chunkman_get_Chunk_at (x, z), nr, nd, nc)); -} - diff --git a/game/src/chunk/chunkman.h b/game/src/chunk/chunkman.h deleted file mode 100644 index a3e2dec..0000000 --- a/game/src/chunk/chunkman.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/* Yes, glib. */ -#include - -#include "chunk.h" -#include "chunkmesh.h" -#include "../gl/program.h" -#include "../entity/entman.h" -#include "../graphics.h" -#include "../gvar.h" -#include "../pakman.h" -#include "../profiler.h" - -#define TERRAIN_HEIGHT 8.0 - -void chunkman_init (); -void chunkman_tick (vec3 position); -void chunkman_render (vec3 position, vec4 planes[6]); - -float chunkman_get_height_at (float x, float z); - -Chunk *chunkman_get_Chunk_at (int x, int z); -void chunkman_get_Chunk_neighbors_at (int x, int z, Chunk **nr, Chunk **nd, Chunk **nc); -void chunkman_set_Chunk_at (int x, int z, Chunk *chunk); - -ChunkMesh *chunkman_get_ChunkMesh_at (int x, int z); -void chunkman_set_ChunkMesh_at (int x, int z, ChunkMesh *mesh); - -void chunkman_generate_Chunk_at (int x, int z); -void chunkman_generate_ChunkMesh_at (int x, int z); diff --git a/game/src/entity/entity.c b/game/src/entity/entity.c index 8188659..9bf33ad 100644 --- a/game/src/entity/entity.c +++ b/game/src/entity/entity.c @@ -1,5 +1,7 @@ #include "entity.h" +extern SimpleMesh mesh_quad1, mesh_quad2, mesh_quad_x; + Entity * Entity_new () { @@ -9,3 +11,115 @@ Entity_new () e->origin = ORIGIN_BOTTOM; /* Usually is. */ return e; } + +gboolean +Entity_render (Entity *entity, vec3 camera_position, mat4 view, vec4 frustum_planes[6]) +{ + if (!entity->visible) + { + return FALSE; + } + + int render_distance = gvar_get ("render_distance"); + gboolean wireframe_enabled = gvar_get ("wireframe_enabled"); + + /* Why an int, why int division? It's simply much faster than + floating point operations. No need to have super accurate cull + distance anyway, and might as well save a lot when rendering entities. */ + unsigned int cull_dist = (render_distance * 16 * entity->cull_distance) / 100; + /* Underneath is the slowest (for the CPU) part of the entity render + process. Moving the camera positions from memory into some + registers. Thus the entity rendering function optimization is + finished, as the last bottleneck is the amount of entities. */ + vec2 c_xz = { camera_position[0], camera_position[2] }; + vec2 e_xz = { entity->position[0], entity->position[2] }; + + if (glm_vec2_distance2 (c_xz, e_xz) > cull_dist*cull_dist && !entity->dont_cull) + { + return FALSE; + } + + vec3 aabb[2]; + entity_compute_aabb (entity, aabb[0], aabb[1]); + + if (!glm_aabb_frustum (aabb, frustum_planes)) + { + return FALSE; + } + + SimpleMesh to_render; + unsigned int render_type = entity->render_type; + + switch (entity->mesh_type) + { + case MESH_QUAD_X: + to_render = mesh_quad_x; + break; + case MESH_QUAD1: + to_render = mesh_quad1; + break; + default: + to_render = mesh_quad2; + break; + } + + mat4 model = GLM_MAT4_IDENTITY_INIT; + + switch (render_type) + { + case RENDER_BILLBOARD: + mat4 inv_view; + glm_mat4_copy (view, inv_view); + glm_mat4_inv (inv_view, inv_view); + + vec3 right = { inv_view[0][0], inv_view[1][0], inv_view[2][0] }; + vec3 up = { inv_view[0][1], inv_view[1][1], inv_view[2][1] }; + vec3 forward = { inv_view[0][2], inv_view[1][2], inv_view[2][2] }; + + mat4 rotation = GLM_MAT4_IDENTITY_INIT; + for (int i = 0; i < 3; ++i) + { + rotation[i][0] = right[i]; + rotation[i][1] = up[i]; + rotation[i][2] = forward[i]; + } + + glm_translate (model, entity->position); + glm_mat4_mul (model, rotation, model); + break; + case RENDER_BILLBOARD_Y: + /* It's not quite a "real" billboard, but the effect is much + better on the eye. */ + vec3 delta; + glm_vec3_sub (camera_position, entity->position, delta); + delta[1] = 0.0; + glm_normalize (delta); + glm_rotate_at(model, entity->position, atan2f (delta[0], delta[2]), (vec3) { 0.0, 1.0, 0.0 }); + glm_translate (model, entity->position); + break; + default: + glm_rotate_at (model, entity->position, glm_rad (entity->rotation[0]), (vec3) { 1.0, 0.0, 0.0 }); + glm_rotate_at (model, entity->position, glm_rad (entity->rotation[1]), (vec3) { 0.0, 1.0, 0.0 }); + glm_rotate_at (model, entity->position, glm_rad (entity->rotation[2]), (vec3) { 0.0, 0.0, 1.0 }); + glm_translate (model, entity->position); + break; + } + + if (entity->origin == ORIGIN_BOTTOM) + { + glm_translate (model, (vec3) { 0.0, entity->scale[1], 0.0 }); + } + + glm_scale (model, entity->scale); + + program_use (entity->program); + program_set_mat4 (entity->program, "model", model); + program_set_float (entity->program, "wavy", entity->wavy); + + texture_use (entity->texture, 0); + + SimpleMesh_bind (&to_render); + SimpleMesh_render (&to_render, wireframe_enabled); + + return TRUE; +} diff --git a/game/src/entity/entity.h b/game/src/entity/entity.h index dd75ec9..e69efa1 100644 --- a/game/src/entity/entity.h +++ b/game/src/entity/entity.h @@ -3,6 +3,11 @@ #include #include +#include "../gvar.h" +#include "../gl/mesh_globals.h" +#include "../gl/program.h" +#include "../gl/texture.h" + typedef struct Entity { enum { MESH_QUAD1, MESH_QUAD2, MESH_QUAD_X } mesh_type; @@ -37,18 +42,19 @@ entity_compute_aabb (Entity *e, vec3 out_min, vec3 out_max) out_max[0] = e->position[0] + half[0]; if (e->origin == ORIGIN_BOTTOM) - { - out_min[1] = e->position[1]; - out_max[1] = e->position[1] + 2.0f * half[1]; - } + { + out_min[1] = e->position[1]; + out_max[1] = e->position[1] + 2.0f * half[1]; + } else - { - out_min[1] = e->position[1] - half[1]; - out_max[1] = e->position[1] + half[1]; - } + { + out_min[1] = e->position[1] - half[1]; + out_max[1] = e->position[1] + half[1]; + } out_min[2] = e->position[2] - half[2]; out_max[2] = e->position[2] + half[2]; } Entity *Entity_new (); +gboolean Entity_render (Entity *entity, vec3 camera_position, mat4 view, vec4 frustum_planes[6]); diff --git a/game/src/entity/entman.c b/game/src/entity/entman.c deleted file mode 100644 index 28372c4..0000000 --- a/game/src/entity/entman.c +++ /dev/null @@ -1,269 +0,0 @@ -#include "entman.h" - -/* 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. */ - -Entity *player_entity, *sky_entity; - -/* The GArray is the fastest of all sequence types. Should work fine - for a billion entities. */ -static GArray *entities; -static GQueue *spawn_requests; - -static SimpleMesh mesh_quad1, mesh_quad2, mesh_quad_x; -static unsigned int last_program = 0; -static unsigned int last_type = 0; -static unsigned int last_texture = 0; - -/* These are defined as statics so we don't get those values from gvar - for every entity... */ -static int render_distance = 2; -static gboolean wireframe_enabled = FALSE; - -static Profiler profiler = { 0 }; - -static const int frame_budget_us = 500; - -void -character_on_tick (Entity *entity) -{ - if (!FLAG_GET ("flight_enabled")) - { - entity->position[1] = chunkman_get_height_at (entity->position[0], entity->position[2]); - } -} - -void -entman_init () -{ - entities = g_array_new (false, false, sizeof (Entity *)); - spawn_requests = g_queue_new (); - mesh_quad1 = SimpleMesh_make_quad1 (); - mesh_quad2 = SimpleMesh_make_quad2 (); - mesh_quad_x = SimpleMesh_make_quad_x (); - - player_entity = Entity_new (); - player_entity->visible = false; - glm_vec3_copy ((vec3) { 1.0, 1.8, 1.0 }, player_entity->scale); - player_entity->on_tick = character_on_tick; - ENTADD (player_entity); - - sky_entity = Entity_new (); - sky_entity->mesh_type = MESH_QUAD1; - sky_entity->origin = ORIGIN_CENTER; - sky_entity->dont_cull = true; - glm_vec3_copy ((vec3) { 5000.0, 5000.0, 5000.0 }, sky_entity->scale); - glm_vec3_copy ((vec3) { 0.0, 200.0, 0.0 }, sky_entity->position); - glm_vec3_copy ((vec3) { 90.0, 0.0, 0.0 }, sky_entity->rotation); - sky_entity->texture = PMTEX ("clouds"); - sky_entity->program = PMPRG ("clouds"); - ENTADD (sky_entity); -} - -void -spawn_queue_process_one () -{ - if (g_queue_is_empty(spawn_requests)) - { - return; - } - - SpawnRequest *req = g_queue_pop_head (spawn_requests); - - Entity *e = ent_templates_clone (req->template_name); - glm_vec3_copy (req->position, e->position); - glm_vec3_copy (req->rotation, e->rotation); - glm_vec3_copy (req->scale, e->scale); - ENTADD (e); - - g_free (req->template_name); - g_free (req); -} - -void -entman_tick () -{ - Profiler_begin (&profiler); - - while (!g_queue_is_empty (spawn_requests)) - { - double elapsed = Profiler_sample (&profiler); - - if (elapsed > frame_budget_us) - { - break; - } - - spawn_queue_process_one (); - } - - /* Handle on tick events. */ - for (guint i = 0; i < entities->len; ++i) - { - Entity *e = g_array_index (entities, Entity*, i); - - if (e->on_tick) - { - e->on_tick (e); - } - } -} - -gboolean -entman_render_entity (Entity *entity, vec3 camera_position, mat4 view, vec4 frustum_planes[6]) -{ - if (!entity->visible) - { - return FALSE; - } - - /* Why an int, why int division? It's simply much faster than - floating point operations. No need to have super accurate cull - distance anyway, and might as well save a lot when rendering entities. */ - unsigned int cull_dist = (render_distance * CHUNK_DIM * entity->cull_distance) / 100; - /* Underneath is the slowest (for the CPU) part of the entity render - process. Moving the camera positions from memory into some - registers. Thus the entity rendering function optimization is - finished, as the last bottleneck is the amount of entities. */ - vec2 c_xz = { camera_position[0], camera_position[2] }; - vec2 e_xz = { entity->position[0], entity->position[2] }; - - if (glm_vec2_distance2 (c_xz, e_xz) > cull_dist*cull_dist && !entity->dont_cull) - { - return FALSE; - } - -#if FRUSTUM_CULL - vec3 aabb[2]; - entity_compute_aabb (entity, aabb[0], aabb[1]); - - if (!glm_aabb_frustum (aabb, frustum_planes)) - { - return FALSE; - } -#endif - - SimpleMesh to_render; - unsigned int render_type = entity->render_type; - - switch (entity->mesh_type) - { - case MESH_QUAD_X: - to_render = mesh_quad_x; - break; - case MESH_QUAD1: - to_render = mesh_quad1; - break; - default: - to_render = mesh_quad2; - break; - } - - mat4 model = GLM_MAT4_IDENTITY_INIT; - - switch (render_type) - { - case RENDER_BILLBOARD: - mat4 inv_view; - glm_mat4_copy (view, inv_view); - glm_mat4_inv (inv_view, inv_view); - - vec3 right = { inv_view[0][0], inv_view[1][0], inv_view[2][0] }; - vec3 up = { inv_view[0][1], inv_view[1][1], inv_view[2][1] }; - vec3 forward = { inv_view[0][2], inv_view[1][2], inv_view[2][2] }; - - mat4 rotation = GLM_MAT4_IDENTITY_INIT; - for (int i = 0; i < 3; ++i) - { - rotation[i][0] = right[i]; - rotation[i][1] = up[i]; - rotation[i][2] = forward[i]; - } - - glm_translate (model, entity->position); - glm_mat4_mul (model, rotation, model); - break; - case RENDER_BILLBOARD_Y: - /* It's not quite a "real" billboard, but the effect is much - better on the eye. */ - vec3 delta; - glm_vec3_sub (camera_position, entity->position, delta); - delta[1] = 0.0; - glm_normalize (delta); - glm_rotate_at(model, entity->position, atan2f (delta[0], delta[2]), (vec3) { 0.0, 1.0, 0.0 }); - glm_translate (model, entity->position); - break; - default: - glm_rotate_at (model, entity->position, glm_rad (entity->rotation[0]), (vec3) { 1.0, 0.0, 0.0 }); - glm_rotate_at (model, entity->position, glm_rad (entity->rotation[1]), (vec3) { 0.0, 1.0, 0.0 }); - glm_rotate_at (model, entity->position, glm_rad (entity->rotation[2]), (vec3) { 0.0, 0.0, 1.0 }); - glm_translate (model, entity->position); - break; - } - - if (entity->origin == ORIGIN_BOTTOM) - { - glm_translate (model, (vec3) { 0.0, entity->scale[1], 0.0 }); - } - - glm_scale (model, entity->scale); - - if (entity->program != last_program) - { - last_program = program_use (entity->program); - } - - program_set_mat4 (entity->program, "model", model); - program_set_float (entity->program, "wavy", entity->wavy); - - if (entity->texture != last_texture) - { - last_texture = texture_use (entity->texture, 0); - } - - if (entity->mesh_type != last_type) - { - SimpleMesh_bind (&to_render); - last_type = entity->mesh_type; - } - - SimpleMesh_render (&to_render, wireframe_enabled); - return TRUE; -} - -void -entman_render (vec3 camera_position, mat4 view, vec4 planes[6]) -{ - int frame_entities = 0; - - render_distance = gvar_get ("render_distance"); - wireframe_enabled = gvar_get ("wireframe_enabled"); - - for (guint i = 0; i < entities->len; ++i) - { - Entity *e = g_array_index (entities, Entity*, i); - frame_entities += entman_render_entity (e, camera_position, view, planes) ? 1 : 0; - } - - gvar_set ("frame_entities", frame_entities); -} - -void -entman_add_entity (Entity *entity) -{ - g_array_append_val (entities, entity); -} - -void -entman_add_spawn_request (char *template_name, vec3 position, vec3 rotation, vec3 scale) -{ - SpawnRequest *req = g_new (SpawnRequest, 1); - req->template_name = g_strdup (template_name); - glm_vec3_copy (position, req->position); - glm_vec3_copy (rotation, req->rotation); - glm_vec3_copy (scale, req->scale); - g_queue_push_tail (spawn_requests, req); -} diff --git a/game/src/entity/entman.h b/game/src/entity/entman.h deleted file mode 100644 index 8c4a818..0000000 --- a/game/src/entity/entman.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -/* TODO: Add a type of spawn request; either TYPE_TEMPLATE, or - TYPE_ENTITY. */ - -#include -#include - -#include "entity.h" -#include "templates.h" -#include "../gl/mesh.h" -#include "../util.h" -#include "../chunk/chunkman.h" -#include "../graphics.h" -#include "../gvar.h" -#include "../pakman.h" -#include "../profiler.h" - -#define ENTADD(entity) (entman_add_entity (entity)) - -typedef struct SpawnRequest -{ - char *template_name; - vec3 position; - vec3 rotation; - vec3 scale; -} SpawnRequest; - -void entman_init (); -void entman_tick (); -void entman_render (vec3 camera_position, mat4 view, vec4 planes[6]); -void entman_add_entity (Entity *entity); -void entman_add_spawn_request (char *template_name, vec3 position, vec3 rotation, vec3 scale); diff --git a/game/src/gl/mesh_globals.c b/game/src/gl/mesh_globals.c new file mode 100644 index 0000000..2ae433b --- /dev/null +++ b/game/src/gl/mesh_globals.c @@ -0,0 +1,15 @@ +#include "mesh_globals.h" + +SimpleMesh mesh_quad1_ui; +SimpleMesh mesh_quad1; +SimpleMesh mesh_quad2; +SimpleMesh mesh_quad_x; + +void +mesh_globals_init () +{ + mesh_quad1_ui = SimpleMesh_make_quad1_ui (); + mesh_quad1 = SimpleMesh_make_quad1 (); + mesh_quad2 = SimpleMesh_make_quad2 (); + mesh_quad_x = SimpleMesh_make_quad_x (); +} diff --git a/game/src/gl/mesh_globals.h b/game/src/gl/mesh_globals.h new file mode 100644 index 0000000..0de132e --- /dev/null +++ b/game/src/gl/mesh_globals.h @@ -0,0 +1,5 @@ +#pragma once + +#include "mesh.h" + +void mesh_globals_init (); diff --git a/game/src/graphics.h b/game/src/graphics.h index e5cbf77..0a17051 100644 --- a/game/src/graphics.h +++ b/game/src/graphics.h @@ -8,8 +8,6 @@ #include "util.h" #include "gvar.h" -#define FRUSTUM_CULL 1 - void graphics_init (); void graphics_prerender (); void graphics_postrender (); diff --git a/game/src/main.c b/game/src/main.c index 61b0038..f69d505 100644 --- a/game/src/main.c +++ b/game/src/main.c @@ -2,7 +2,6 @@ #include #include -#include "chunk/chunkman.h" #include "entity/templates.h" #include "entity/entity_guile.h" #include "ui/widget_guile.h" @@ -11,6 +10,8 @@ #include "pakman.h" #include "stage/stageman.h" +#include "gl/mesh_globals.h" + SDL_AppResult SDL_AppInit (void **appstate, int argc, char **argv) { @@ -36,14 +37,10 @@ SDL_AppInit (void **appstate, int argc, char **argv) pakman_load_pak ("res/"); stageman_init (); - - /* I don't know. This probably needs to be moved somewhere. */ - ui_widget_mesh_init (); + mesh_globals_init (); /* The UI, entity and chunk subsystems. */ ent_templates_init (); - entman_init (); - chunkman_init (); stageman_switch_stage ("main_menu"); diff --git a/game/src/stage/stage_common.c b/game/src/stage/stage_common.c index 01959cd..07d93a3 100644 --- a/game/src/stage/stage_common.c +++ b/game/src/stage/stage_common.c @@ -6,7 +6,10 @@ GameStage_new () GameStage *g = g_new0 (GameStage, 1); g->ui_context = UIContext_new (); g->ui_context->tooltip = PMUID ("tooltip"); + + g->world = World_new (); g->camera = g_new0 (Camera, 1); + return g; } @@ -18,14 +21,14 @@ GameStage_register_UIContext_handlers (GameStage *self) christ, it's so complex... */ #define GAEH GameStage_add_event_handler - GAEH (self, 1, SDL_EVENT_WINDOW_RESIZED, NULL, NULL, UIContext_window_resize,self->ui_context); - GAEH (self, 1, SDL_EVENT_KEY_DOWN, evt_pred_flag, "ui_has_focus", UIContext_key_down, self->ui_context); - GAEH (self, 1, SDL_EVENT_KEY_UP, evt_pred_flag, "ui_has_focus", UIContext_key_up, self->ui_context); - GAEH (self, 1, SDL_EVENT_MOUSE_MOTION, evt_pred_flag, "ui_has_focus", UIContext_mouse_motion, self->ui_context); - GAEH (self, 1, SDL_EVENT_MOUSE_BUTTON_DOWN, evt_pred_flag, "ui_has_focus", UIContext_button_down, self->ui_context); - GAEH (self, 1, SDL_EVENT_MOUSE_BUTTON_UP, evt_pred_flag, "ui_has_focus", UIContext_button_up, self->ui_context); - GAEH (self, 1, SDL_EVENT_MOUSE_WHEEL, evt_pred_flag, "ui_has_focus", UIContext_mouse_wheel, self->ui_context); - GAEH (self, 1, SDL_EVENT_TEXT_INPUT, evt_pred_flag, "ui_has_focus", UIContext_text_input, self->ui_context); + GAEH (self, 1, SDL_EVENT_WINDOW_RESIZED, NULL, NULL, UIContext_window_resize, self->ui_context); + GAEH (self, 1, SDL_EVENT_KEY_DOWN, evt_pred_flag, "ui_has_focus", UIContext_key_down, self->ui_context); + GAEH (self, 1, SDL_EVENT_KEY_UP, evt_pred_flag, "ui_has_focus", UIContext_key_up, self->ui_context); + GAEH (self, 1, SDL_EVENT_MOUSE_MOTION, evt_pred_flag, "ui_has_focus", UIContext_mouse_motion, self->ui_context); + GAEH (self, 1, SDL_EVENT_MOUSE_BUTTON_DOWN, evt_pred_flag, "ui_has_focus", UIContext_button_down, self->ui_context); + GAEH (self, 1, SDL_EVENT_MOUSE_BUTTON_UP, evt_pred_flag, "ui_has_focus", UIContext_button_up, self->ui_context); + GAEH (self, 1, SDL_EVENT_MOUSE_WHEEL, evt_pred_flag, "ui_has_focus", UIContext_mouse_wheel, self->ui_context); + GAEH (self, 1, SDL_EVENT_TEXT_INPUT, evt_pred_flag, "ui_has_focus", UIContext_text_input, self->ui_context); #undef GAEH } diff --git a/game/src/stage/stage_common.h b/game/src/stage/stage_common.h index e278dad..8689515 100644 --- a/game/src/stage/stage_common.h +++ b/game/src/stage/stage_common.h @@ -8,6 +8,7 @@ #include "../util.h" #include "../gvar.h" #include "../camera.h" +#include "../world.h" typedef struct { unsigned int on_event; @@ -21,10 +22,10 @@ typedef struct GameStage { UIContext *ui_context; /* Could be NULL... */ Camera *camera; /* Doesn't have to be defined. */ - - GList *event_handlers; - void *userdata; + World *world; + GList *event_handlers; + void (*on_enter) (struct GameStage *); void (*on_exit) (struct GameStage *); void (*on_update) (struct GameStage *); diff --git a/game/src/stage/stage_ingame.c b/game/src/stage/stage_ingame.c index 74d1f75..39111f4 100644 --- a/game/src/stage/stage_ingame.c +++ b/game/src/stage/stage_ingame.c @@ -1,7 +1,6 @@ #include "stage_ingame.h" extern SDL_Window *sdl_window; -extern Entity *player_entity, *sky_entity; static Profiler profiler = { 0 }; static const int profiler_check_freq_s = 10; @@ -10,6 +9,8 @@ static const int render_frame_budget_us = 16666; static const int render_distance_min = 2; static const int render_distance_max = 12; +static Entity *player_entity, *sky_entity; + static void _on_enter () { @@ -19,9 +20,6 @@ _on_enter () static void _on_update (GameStage *self) { - chunkman_tick (self->camera->position); - entman_tick (); - /* We pin the camera to the player position, with eye level 20 centimeters below the height. */ glm_vec3_copy (player_entity->position, self->camera->position); @@ -33,6 +31,8 @@ _on_update (GameStage *self) sky_entity->position[0] = self->camera->position[0]; sky_entity->position[2] = self->camera->position[2]; + World_update (self->world, self->camera->position); + int speed = 5; float delta_time = gvar_get ("delta_time"); @@ -109,14 +109,11 @@ _on_render (GameStage *self) pakman_all_paks_set_float ("time", gvar_get ("last_time")); 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 (self->camera->position, planes); - entman_render (self->camera->position, view, planes); + World_render (self->world, self->camera->position, view, planes); Profiler_sample (&profiler); @@ -198,6 +195,23 @@ stage_ingame_new () /* If your GameStage uses the UI, always call this. */ GameStage_register_UIContext_handlers (self); self->ui_context->root = PMUID ("debug_window"); + + player_entity = Entity_new (); + player_entity->visible = false; + glm_vec3_copy ((vec3) { 1.0, 1.8, 1.0 }, player_entity->scale); + /* player_entity->on_tick = character_on_tick; */ + World_add_Entity (self->world, player_entity); + + sky_entity = Entity_new (); + sky_entity->mesh_type = MESH_QUAD1; + sky_entity->origin = ORIGIN_CENTER; + sky_entity->dont_cull = true; + glm_vec3_copy ((vec3) { 5000.0, 5000.0, 5000.0 }, sky_entity->scale); + glm_vec3_copy ((vec3) { 0.0, 200.0, 0.0 }, sky_entity->position); + glm_vec3_copy ((vec3) { 90.0, 0.0, 0.0 }, sky_entity->rotation); + sky_entity->texture = PMTEX ("clouds"); + sky_entity->program = PMPRG ("clouds"); + World_add_Entity (self->world, sky_entity); return self; } diff --git a/game/src/stage/stage_ingame.h b/game/src/stage/stage_ingame.h index bcf7547..f6c52c0 100644 --- a/game/src/stage/stage_ingame.h +++ b/game/src/stage/stage_ingame.h @@ -7,7 +7,5 @@ #include "../profiler.h" #include "../graphics.h" -#include "../entity/entman.h" -#include "../chunk/chunkman.h" GameStage *stage_ingame_new (); diff --git a/game/src/ui/widget_render.c b/game/src/ui/widget_render.c index 91e561a..d2de2f6 100644 --- a/game/src/ui/widget_render.c +++ b/game/src/ui/widget_render.c @@ -1,12 +1,6 @@ #include "widget_render.h" -static SimpleMesh quad; - -void -ui_widget_mesh_init () -{ - quad = SimpleMesh_make_quad1_ui (); -} +extern SimpleMesh mesh_quad1_ui; static inline void _Widget_get_aabb (Widget *self, vec2 aabb[2]) @@ -52,7 +46,7 @@ _Widget_render_text_line(const char *text, TextAlignment alignment, ivec2 positi glm_scale2d (model, (vec2) { size[0], size[1] }); program_set_mat3 (PMPRG ("uitext"), "model", model); - SimpleMesh_render (&quad, FALSE); + SimpleMesh_render (&mesh_quad1_ui, FALSE); xoff += size[0]; p = g_utf8_next_char (p); @@ -69,7 +63,7 @@ Widget_render_base (Widget *self, mat4 projection, int window_height) if (self->parent == NULL) { - SimpleMesh_bind (&quad); + SimpleMesh_bind (&mesh_quad1_ui); } const WidgetStyle *style = self->style; @@ -132,7 +126,7 @@ Widget_render_base (Widget *self, mat4 projection, int window_height) /* X, Y is the lower left corner of the box. */ glScissor (x, y, w, h); - SimpleMesh_render (&quad, FALSE); + SimpleMesh_render (&mesh_quad1_ui, FALSE); } void @@ -277,7 +271,7 @@ Widget_render_entry (Widget *self, mat4 projection, int window_height) program_set_mat4 (PMPRG ("uidummy"), "projection", projection); program_set_vec4 (PMPRG ("uidummy"), "background_color", self->style->text_color); - SimpleMesh_render (&quad, FALSE); + SimpleMesh_render (&mesh_quad1_ui, FALSE); } } diff --git a/game/src/ui/widget_render.h b/game/src/ui/widget_render.h index 3e07e1c..127648c 100644 --- a/game/src/ui/widget_render.h +++ b/game/src/ui/widget_render.h @@ -5,7 +5,7 @@ #include "widget_fw.h" #include "widget_core.h" -void ui_widget_mesh_init (); +#include "../gl/mesh_globals.h" void Widget_render_base (Widget *self, mat4 projection, int window_height); void Widget_render_label (Widget *self, mat4 projection, int window_height); diff --git a/game/src/world.c b/game/src/world.c new file mode 100644 index 0000000..3ba36fd --- /dev/null +++ b/game/src/world.c @@ -0,0 +1,307 @@ +#include "world.h" + +guint +_ivec2_hash (gconstpointer key) +{ + /* This makes sure there's no collisions, thus making the whole + table much, much faster. */ + const ivec2 *v = key; + guint x = (guint) (*v)[0]; + guint y = (guint) (*v)[1]; + return (x * 73856093u) ^ (y * 19349663u); +} + +gboolean +_ivec2_equal (gconstpointer a, gconstpointer b) +{ + return memcmp (a, b, sizeof (int) * 2) == 0; +} + +ivec2 * +_make_key(int x, int z) +{ + ivec2 key = { x, z }; + return g_memdup2 (&key, sizeof (ivec2)); +} + +World * +World_new () +{ + World *w = g_new0 (World, 1); + + w->entities = g_array_new (FALSE, FALSE, sizeof (Entity *)); + w->chunks = g_hash_table_new_full (_ivec2_hash, _ivec2_equal, free, (GDestroyNotify) Chunk_free); + /* Chunk_free is wrong here probably... */ + w->chunk_meshes = g_hash_table_new_full (_ivec2_hash, _ivec2_equal, free, (GDestroyNotify) Chunk_free); + + return w; +} + +void +_world_to_chunk (float x, float z, int *cx, int *cz) +{ + *cx = floor (x / (float) CHUNK_DIM); + *cz = floor (z / (float) CHUNK_DIM); +} + +void +World_update (World *w, vec3 position) +{ + int render_distance = gvar_get ("render_distance"); + int cx, cz; + _world_to_chunk (position[0], position[2], &cx, &cz); + + 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 (World_get_Chunk_at (w, x, z) == NULL) + { + World_generate_Chunk_at (w, x, z); + } + } + } + + for (int z = cz - render_distance; z <= cz + render_distance; ++z) + { + for (int x = cx - render_distance; x <= cx + render_distance; ++x) + { + if (World_get_ChunkMesh_at (w, x, z) == NULL) + { + World_generate_ChunkMesh_at (w, x, z); + } + } + } + + /* Handle on tick events. */ + for (guint i = 0; i < w->entities->len; ++i) + { + Entity *e = g_array_index (w->entities, Entity*, i); + + if (e->on_tick) + { + e->on_tick (e); + } + } +} + +void +World_render (World *w, vec3 position, mat4 view, vec4 planes[6]) +{ + int cx = 0, cz = 0; + _world_to_chunk (position[0], position[2], &cx, &cz); + + program_use (PMPRG ("chunk")); + + texture_use (PMTEX ("terrain0"), 0); + texture_use (PMTEX ("terrain1"), 1); + texture_use (PMTEX ("terrain2"), 2); + texture_use (PMTEX ("terrain3"), 3); + texture_use (PMTEX ("terrain4"), 4); + + int render_distance = gvar_get ("render_distance"); + int frame_chunks = 0, frame_entities = 0; + + for (int z = cz - render_distance; z <= cz + render_distance; ++z) + { + for (int x = cx - render_distance; x <= cx + render_distance; ++x) + { + /* Render in a circle instead of a square. */ + if ((x - cx) * (x - cx) + (z - cz) * (z - cz) > render_distance*render_distance) + { + continue; + } + + ChunkMesh *mesh = World_get_ChunkMesh_at (w, x, z); + if (mesh != NULL) + { + 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; + } + + ChunkMesh_bind (mesh); + program_set_vec3_ind (PMPRG ("chunk"), "chunk_position", x * CHUNK_DIM, 0.0, z * CHUNK_DIM); + ChunkMesh_render (mesh, FLAG_GET ("wireframe_enabled")); + frame_chunks++; + } + } + } + + for (guint i = 0; i < w->entities->len; ++i) + { + Entity *ent = g_array_index (w->entities, Entity*, i); + frame_entities += Entity_render (ent, position, view, planes) ? 1 : 0; + } + + gvar_set ("frame_chunks", frame_chunks); + gvar_set ("frame_entities", frame_entities); +} + +/* Entity-specific */ +void +World_add_Entity (World *w, Entity *e) +{ + g_array_append_val (w->entities, e); +} + +/* Chunk-specific */ +static inline float +_Chunk_get_height (Chunk *c, int x, int z) +{ + return c ? c->heightmap[INDEX (x, z)] : 0.0; +} + +float +World_get_height_at (World *w, float x, float z) +{ + int cx, cz; + _world_to_chunk (x, z, &cx, &cz); + Chunk *chunk = World_get_Chunk_at (w, cx, cz); + + if (chunk == NULL) + { + return 0.0; + } + + float lx = x - cx * CHUNK_DIM; + float lz = z - cz * CHUNK_DIM; + + int x0 = (int) floor (lx); + int z0 = (int) floor (lz); + int x1 = x0 + 1; + int z1 = z0 + 1; + + float tx = lx - x0; + float tz = lz - z0; + + Chunk *r = NULL, *d = NULL, *c = NULL; + World_get_Chunk_neighbors_at (w, cx, cz, &r, &d, &c); + + float h00 = _Chunk_get_height (chunk, x0, z0); + float h10 = (x1 < CHUNK_DIM) ? _Chunk_get_height (chunk, x1, z0) : + (r ? _Chunk_get_height (r, 0, z0) : h00); + float h01 = (z1 < CHUNK_DIM) ? _Chunk_get_height (chunk, x0, z1) : + (d ? _Chunk_get_height (d, x0, 0) : h00); + float h11; + + if (x1 < CHUNK_DIM && z1 < CHUNK_DIM) + { + h11 = _Chunk_get_height (chunk, x1, z1); + } + else if (x1 >= CHUNK_DIM && z1 < CHUNK_DIM) + { + h11 = r ? _Chunk_get_height (r, 0, z1) : h10; + } + else if (x1 < CHUNK_DIM && z1 >= CHUNK_DIM) + { + h11 = d ? _Chunk_get_height (d, x1, 0) : h01; + } + else + { + h11 = c ? _Chunk_get_height (c, 0, 0) : h00; + } + + // Bilinear interpolation. + float h0 = h00 * (1 - tx) + h10 * tx; + float h1 = h01 * (1 - tx) + h11 * tx; + float height = h0 * (1 - tz) + h1 * tz; + + return height; +} + +Chunk * +World_get_Chunk_at (World *w, int x, int z) +{ + ivec2 key = { x, z }; + return (Chunk *) g_hash_table_lookup (w->chunks, &key); +} + +void +World_get_Chunk_neighbors_at (World *w, int x, int z, Chunk **nr, Chunk **nd, Chunk **nc) +{ + *nr = World_get_Chunk_at (w, x + 1, z); + *nd = World_get_Chunk_at (w, x, z + 1); + *nc = World_get_Chunk_at (w, x + 1, z + 1); +} + +void +World_set_Chunk_at (World *w, int x, int z, Chunk *chunk) +{ + g_hash_table_insert (w->chunks, _make_key (x, z), chunk); +} + +ChunkMesh * +World_get_ChunkMesh_at (World *w, int x, int z) +{ + ivec2 key = { x, z }; + return (ChunkMesh *) g_hash_table_lookup (w->chunk_meshes, key); +} + +void +World_set_ChunkMesh_at (World *w, int x, int z, ChunkMesh *mesh) +{ + g_hash_table_insert (w->chunk_meshes, _make_key (x, z), mesh); +} + +void +World_generate_Chunk_at (World *w, int cx, int cz) +{ + Chunk *chunk = Chunk_new (); + int xoffset = cx * CHUNK_DIM; + int zoffset = cz * CHUNK_DIM; + + for (int z = 0; z < CHUNK_DIM; ++z) + { + for (int x = 0; x < CHUNK_DIM; ++x) + { + chunk->heightmap[INDEX (x, z)] = + (glm_perlin_vec2 + ((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.333) / 20.0, (z + zoffset + 0.333) / 20.0 }) + 0.6 * 5) % 5; + + if (rand() % 100 < 2) + { + Entity *e = ent_templates_clone ("tree"); + glm_vec3_copy ((vec3) { x + xoffset, chunk->heightmap[INDEX (x, z)], z + zoffset }, e->position); + glm_vec3_copy ((vec3) { 0.0, rand () % 180, 0.0 }, e->rotation); + glm_vec3_copy ((vec3) { 3.0, (rand () % 5) + 5, 3.0 }, e->scale); + World_add_Entity (w, e); + } + + if (rand() % 100 < 10) + { + Entity *e = ent_templates_clone ("grass"); + glm_vec3_copy ((vec3) { x + xoffset, chunk->heightmap[INDEX (x, z)], z + zoffset }, e->position); + glm_vec3_copy ( (vec3) { 0.0, 0.0, 0.0 }, e->rotation); + glm_vec3_copy ( (vec3) { 1.0, 1.0, 1.0 }, e->scale); + World_add_Entity (w, e); + } + + } + } + + World_set_Chunk_at (w, cx, cz, chunk); +} + +void +World_generate_ChunkMesh_at (World *w, int x, int z) +{ + Chunk *nr, *nd, *nc; + World_get_Chunk_neighbors_at (w, x, z, &nr, &nd, &nc); + + g_assert (nr != NULL); + g_assert (nd != NULL); + g_assert (nc != NULL); + + World_set_ChunkMesh_at + (w, x, z, ChunkMesh_build (World_get_Chunk_at (w, x, z), nr, nd, nc)); +} + diff --git a/game/src/world.h b/game/src/world.h new file mode 100644 index 0000000..0cc97b4 --- /dev/null +++ b/game/src/world.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +#include "util.h" +#include "gvar.h" +#include "pakman.h" + +#include "entity/entity.h" +#include "entity/templates.h" +#include "chunk/chunk.h" +#include "chunk/chunkmesh.h" + +#define TERRAIN_HEIGHT 8.0 + +typedef struct World +{ + GArray *entities; + GHashTable *chunks, *chunk_meshes; +} World; + +World *World_new (); +void World_update (World *w, vec3 position); +void World_render (World *w, vec3 position, mat4 view, vec4 frustum[6]); + +/* Entity-specific */ +void World_add_Entity (World *w, Entity *e); + +/* Chunk-specific */ +float World_get_height_at (World *w, float x, float z); + +Chunk *World_get_Chunk_at (World *w, int x, int z); +void World_get_Chunk_neighbors_at (World *w, int x, int z, Chunk **nr, Chunk **nd, Chunk **nc); +void World_set_Chunk_at (World *w, int x, int z, Chunk *chunk); + +ChunkMesh *World_get_ChunkMesh_at (World *w, int x, int z); +void World_set_ChunkMesh_at (World *w, int x, int z, ChunkMesh *mesh); + +void World_generate_Chunk_at (World *w, int cx, int cz); +void World_generate_ChunkMesh_at (World *w, int cx, int cz); +