231 lines
5.4 KiB
C
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);
|
|
}
|