Files
wschod/game/src/chunk/chunkman.c
T

311 lines
7.2 KiB
C

#include "chunkman.h"
extern int render_distance;
int n_frame_chunks;
static GHashTable *chunks;
static GHashTable *chunk_meshes;
static bool bound = false;
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);
uint64_t start = SDL_GetPerformanceCounter ();
uint64_t freq = SDL_GetPerformanceFrequency ();
double elapsed_us = 0;
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)
{
uint64_t now = SDL_GetPerformanceCounter ();
elapsed_us = (double) (now - start) * 1000000.0 / (double) freq;
if (elapsed_us > CHUNK_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)
{
uint64_t now = SDL_GetPerformanceCounter ();
elapsed_us = (double) (now - start) * 1000000.0 / (double) freq;
if (elapsed_us > CHUNK_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 (RESPRG ("chunk"));
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, RESTEX ("terrain0"));
glActiveTexture (GL_TEXTURE1);
glBindTexture (GL_TEXTURE_2D, RESTEX ("terrain1"));
glActiveTexture (GL_TEXTURE2);
glBindTexture (GL_TEXTURE_2D, RESTEX ("terrain2"));
glActiveTexture (GL_TEXTURE3);
glBindTexture (GL_TEXTURE_2D, RESTEX ("terrain3"));
glActiveTexture (GL_TEXTURE4);
glBindTexture (GL_TEXTURE_2D, RESTEX ("terrain4"));
n_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 (RESPRG ("chunk"), "chunk_position", x * CHUNK_DIM, 0.0, z * CHUNK_DIM);
ChunkMesh_render (mesh, flags_get ("wireframe_enabled"));
n_frame_chunks++;
}
}
}
bound = false;
}
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));
}