Flatten chunk and entity managers
This commit is contained in:
@@ -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));
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/* Yes, glib. */
|
||||
#include <glib-2.0/glib.h>
|
||||
|
||||
#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);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
#include <glib-2.0/glib.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#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;
|
||||
@@ -52,3 +57,4 @@ entity_compute_aabb (Entity *e, vec3 out_min, vec3 out_max)
|
||||
}
|
||||
|
||||
Entity *Entity_new ();
|
||||
gboolean Entity_render (Entity *entity, vec3 camera_position, mat4 view, vec4 frustum_planes[6]);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/* TODO: Add a type of spawn request; either TYPE_TEMPLATE, or
|
||||
TYPE_ENTITY. */
|
||||
|
||||
#include <glib-2.0/glib.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#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);
|
||||
@@ -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 ();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "mesh.h"
|
||||
|
||||
void mesh_globals_init ();
|
||||
@@ -8,8 +8,6 @@
|
||||
#include "util.h"
|
||||
#include "gvar.h"
|
||||
|
||||
#define FRUSTUM_CULL 1
|
||||
|
||||
void graphics_init ();
|
||||
void graphics_prerender ();
|
||||
void graphics_postrender ();
|
||||
|
||||
+3
-6
@@ -2,7 +2,6 @@
|
||||
#include <SDL3/SDL_main.h>
|
||||
#include <libguile.h>
|
||||
|
||||
#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");
|
||||
|
||||
|
||||
@@ -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,7 +21,7 @@ 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_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);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "../util.h"
|
||||
#include "../gvar.h"
|
||||
#include "../camera.h"
|
||||
#include "../world.h"
|
||||
|
||||
typedef struct {
|
||||
unsigned int on_event;
|
||||
@@ -21,9 +22,9 @@ typedef struct GameStage
|
||||
{
|
||||
UIContext *ui_context; /* Could be NULL... */
|
||||
Camera *camera; /* Doesn't have to be defined. */
|
||||
World *world;
|
||||
|
||||
GList *event_handlers;
|
||||
void *userdata;
|
||||
|
||||
void (*on_enter) (struct GameStage *);
|
||||
void (*on_exit) (struct GameStage *);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -199,5 +196,22 @@ stage_ingame_new ()
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,5 @@
|
||||
#include "../profiler.h"
|
||||
#include "../graphics.h"
|
||||
|
||||
#include "../entity/entman.h"
|
||||
#include "../chunk/chunkman.h"
|
||||
|
||||
GameStage *stage_ingame_new ();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <glib-2.0/glib.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#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);
|
||||
|
||||
Reference in New Issue
Block a user