Files
wschod/game/src/entity/entman.c
T

231 lines
5.4 KiB
C

#include "entman.h"
extern vec3 camera_position;
Entity *player_entity;
static GSequence *entities;
static EntityMesh mesh_quad1, mesh_quad2, mesh_quad_x;
static unsigned int last_type = -1;
static unsigned int last_texture = -1;
void
character_on_tick (Entity *entity)
{
entity->position[1] = chunkman_get_height_at (entity->position[0], entity->position[2]);
}
void
entman_init ()
{
entities = g_sequence_new (NULL);
mesh_quad1 = EntityMesh_make_quad1 ();
mesh_quad2 = EntityMesh_make_quad2 ();
mesh_quad_x = EntityMesh_make_quad_x ();
player_entity = malloc (sizeof (Entity));
*player_entity = (Entity) {
.origin = ORIGIN_BOTTOM,
.visible = false,
.position = { 0.0 },
.scale = { 1.0, 1.8, 1.0 },
.on_tick = character_on_tick
};
ENTADD (player_entity);
Entity *sky = malloc (sizeof (Entity));
*sky = (Entity) {
.mesh_type = MESH_QUAD1,
.origin = ORIGIN_CENTER,
.visible = true,
.dont_cull = true,
.position = { 0.0, 200.0, 0.0 },
.scale = { 5000.0, 5000.0, 5000.0 },
.rotation = { 90.0 },
.texture = RESTEX ("clouds")
};
ENTADD (sky);
}
void
entman_tick_internal (gpointer data, gpointer userdata)
{
UNUSED (userdata);
Entity *entity = data;
if (entity->on_tick)
{
entity->on_tick (entity);
}
}
void
entman_tick ()
{
g_sequence_foreach (entities, entman_tick_internal, NULL);
}
void
entman_render_internal (gpointer data, gpointer userdata)
{
Entity *entity = data;
mat4 *view = userdata;
if (!entity->visible)
{
return;
}
if (glm_vec3_distance (camera_position, entity->position) > ENTITY_RENDER_DISTANCE
&& !entity->dont_cull)
{
return;
}
/* TODO: Fix this up. It still isn't perfect, when you look up,
sometimes the entities get culled. */
#if FRUSTUM_CULL
vec4 planes[6];
mat4 proj, view_proj;
camera_get_projection (640.0 / 480.0, proj);
glm_mat4_mul (proj, *view, view_proj);
glm_frustum_planes (view_proj, planes);
vec3 aabb[2];
entity_compute_aabb (entity, aabb[0], aabb[1]);
if (!glm_aabb_frustum (aabb, planes))
{
return;
}
#endif
EntityMesh 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_set_mat4 (RESPRG ("default"), "model", model);
if (entity->texture != last_texture)
{
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, entity->texture);
}
if (entity->mesh_type != last_type)
{
EntityMesh_bind (&to_render);
}
last_type = entity->mesh_type;
last_texture = entity->texture;
EntityMesh_render (&to_render);
}
void
entman_render (mat4 view)
{
g_sequence_foreach (entities, entman_render_internal, view);
}
static inline int
entman_compare (gconstpointer a, gconstpointer b, gpointer userdata)
/* 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;
if (entity1->mesh_type == entity2->mesh_type)
{
return 0;
}
// Handle MESH_QUAD1
if (entity1->mesh_type == MESH_QUAD1)
return -1;
if (entity2->mesh_type == MESH_QUAD1)
return 1;
// Handle MESH_QUAD_X vs MESH_QUAD2
if (entity1->mesh_type == MESH_QUAD_X && entity2->mesh_type == MESH_QUAD2)
return -1;
if (entity1->mesh_type == MESH_QUAD2 && entity2->mesh_type == MESH_QUAD_X)
return 1;
// Default: compare numerically
return (int) entity1->mesh_type - (int) entity2->mesh_type;
}
void
entman_add_entity (Entity *entity)
{
g_sequence_insert_sorted (entities, entity, entman_compare, NULL);
}